Index: wp-admin/admin-ajax.php
===================================================================
--- wp-admin/admin-ajax.php	(revision 15818)
+++ wp-admin/admin-ajax.php	(working copy)
@@ -683,9 +683,11 @@
 
 	$status = $wpdb->get_var( $wpdb->prepare("SELECT post_status FROM $wpdb->posts WHERE ID = %d", $comment_post_ID) );
 
-	if ( empty($status) )
+	$post_status_obj = get_post_status_object($status);
+	
+	if ( empty($post_status_obj) )
 		die('1');
-	elseif ( in_array($status, array('draft', 'pending', 'trash') ) )
+	elseif ( ! $post_status_obj->public && ! $post_status_obj->private )
 		die( __('Error: you are replying to a comment on a draft post.') );
 
 	$user = wp_get_current_user();
@@ -1181,10 +1183,26 @@
 	if ( isset($data['post_parent']) )
 		$data['parent_id'] = $data['post_parent'];
 
+	$post_type_object = get_post_type_object( $post['post_type'] );
+
 	// status
-	if ( isset($data['keep_private']) && 'private' == $data['keep_private'] )
-		$data['post_status'] = 'private';
-	else
+	foreach( get_post_stati( array( 'internal' => false, 'object_type' => $post['post_type'] ), 'object' ) as $_status => $_status_obj ) {
+		if ( ( 'publish' == $_status ) || ( ! $_status_obj->private && ! $_status_obj->public ) ) // private and custom public stati only here
+			continue;
+
+		if ( isset($data["keep_{$_status}"]) ) {
+			$set_status_cap = "set_{$_status}_posts";
+			$check_cap = ( ! empty( $post_type_object->cap->$set_status_cap ) ) ? $post_type_object->cap->$set_status_cap : $post_type_object->cap->publish_posts;
+
+			if ( current_user_can( $check_cap ) ) {
+				$data['post_status'] = $_status;
+				$keeping_private = true;
+				break;
+			}	
+		}
+	}
+
+	if ( empty($keeping_private) ) // private or custom public status selected and granted
 		$data['post_status'] = $data['_status'];
 
 	if ( empty($data['comment_status']) )
@@ -1258,30 +1276,30 @@
 	if ( count($search_terms) > 1 && $search_terms[0] != $s )
 		$search .= " OR ($wpdb->posts.post_title LIKE '%{$term}%') OR ($wpdb->posts.post_content LIKE '%{$term}%')";
 
-	$posts = $wpdb->get_results( "SELECT ID, post_title, post_status, post_date FROM $wpdb->posts WHERE post_type = '$what' AND post_status IN ('draft', 'publish') AND ($search) ORDER BY post_date_gmt DESC LIMIT 50" );
+	$public_stati = apply_filters( 'find_posts_stati', get_post_stati( array( 'public' => true ) ) );
+	$public_csv = implode( "', '", $public_stati );
 
+	$posts = $wpdb->get_results( "SELECT ID, post_title, post_status, post_date FROM $wpdb->posts WHERE post_type = '$what' AND post_status IN ('draft', '$public_csv') AND ($search) ORDER BY post_date_gmt DESC LIMIT 50" );
+
 	if ( ! $posts ) {
 		$posttype = get_post_type_object($what);
 		exit($posttype->labels->not_found);
 	}
 
+	$stat = array();
+	
 	$html = '<table class="widefat" cellspacing="0"><thead><tr><th class="found-radio"><br /></th><th>'.__('Title').'</th><th>'.__('Date').'</th><th>'.__('Status').'</th></tr></thead><tbody>';
 	foreach ( $posts as $post ) {
+		
+		if ( ! isset( $stat[$post->post_status] ) ) {
+			$_status_obj = get_post_status_object( $post->post_status );
+			
+			if ( ! $_status_obj )
+				$_status_obj = get_post_status_object( 'draft' );
+			elseif ( ( $_status_obj->public || $_status_obj->private ) &&  ( 'publish' != $post->post_status ) )
+				$_status_obj = get_post_status_object( 'publish' );
 
-		switch ( $post->post_status ) {
-			case 'publish' :
-			case 'private' :
-				$stat = __('Published');
-				break;
-			case 'future' :
-				$stat = __('Scheduled');
-				break;
-			case 'pending' :
-				$stat = __('Pending Review');
-				break;
-			case 'draft' :
-				$stat = __('Draft');
-				break;
+			$stat[$post->post_status] = $_status_obj->labels->caption;
 		}
 
 		if ( '0000-00-00 00:00:00' == $post->post_date ) {
@@ -1292,7 +1310,7 @@
 		}
 
 		$html .= '<tr class="found-posts"><td class="found-radio"><input type="radio" id="found-'.$post->ID.'" name="found_post_id" value="' . esc_attr($post->ID) . '"></td>';
-		$html .= '<td><label for="found-'.$post->ID.'">'.esc_html( $post->post_title ).'</label></td><td>'.esc_html( $time ).'</td><td>'.esc_html( $stat ).'</td></tr>'."\n\n";
+		$html .= '<td><label for="found-'.$post->ID.'">'.esc_html( $post->post_title ).'</label></td><td>'.esc_html( $time ).'</td><td>'.esc_html( $stat[$post->post_status] ).'</td></tr>'."\n\n";
 	}
 	$html .= '</tbody></table>';
 
Index: wp-admin/edit-form-advanced.php
===================================================================
--- wp-admin/edit-form-advanced.php	(revision 15818)
+++ wp-admin/edit-form-advanced.php	(working copy)
@@ -49,6 +49,7 @@
 		// translators: Publish box date format, see http://php.net/date
 		date_i18n( __( 'M j, Y @ G:i' ), strtotime( $post->post_date ) ), esc_url( get_permalink($post_ID) ) ),
 	10 => sprintf( __('Post draft updated. <a target="_blank" href="%s">Preview post</a>'), esc_url( add_query_arg( 'preview', 'true', get_permalink($post_ID) ) ) ),
+	11 => sprintf( __('Post moderated. <a target="_blank" href="%s">Preview post</a>'), esc_url( add_query_arg( 'preview', 'true', get_permalink($post_ID) ) ) ),
 );
 $messages['page'] = array(
 	 0 => '', // Unused. Messages start at index 1.
@@ -62,6 +63,7 @@
 	 8 => sprintf( __('Page submitted. <a target="_blank" href="%s">Preview page</a>'), esc_url( add_query_arg( 'preview', 'true', get_permalink($post_ID) ) ) ),
 	 9 => sprintf( __('Page scheduled for: <strong>%1$s</strong>. <a target="_blank" href="%2$s">Preview page</a>'), date_i18n( __( 'M j, Y @ G:i' ), strtotime( $post->post_date ) ), esc_url( get_permalink($post_ID) ) ),
 	10 => sprintf( __('Page draft updated. <a target="_blank" href="%s">Preview page</a>'), esc_url( add_query_arg( 'preview', 'true', get_permalink($post_ID) ) ) ),
+	11 => sprintf( __('Page moderated. <a target="_blank" href="%s">Preview page</a>'), esc_url( add_query_arg( 'preview', 'true', get_permalink($post_ID) ) ) ),
 );
 
 $messages = apply_filters( 'post_updated_messages', $messages );
@@ -142,10 +144,13 @@
 if ( post_type_supports($post_type, 'comments') )
 	add_meta_box('commentstatusdiv', __('Discussion'), 'post_comment_status_meta_box', $post_type, 'normal', 'core');
 
-if ( ('publish' == $post->post_status || 'private' == $post->post_status) && post_type_supports($post_type, 'comments') )
+if ( ! $post_status_object = get_post_status_object( $post->post_status ) )
+	$post_status_object = get_post_status_object( 'draft' );
+
+if ( ( $post_status_object->public || $post_status_object->private ) && post_type_supports($post_type, 'comments') )
 	add_meta_box('commentsdiv', __('Comments'), 'post_comment_meta_box', $post_type, 'normal', 'core');
 
-if ( !( 'pending' == $post->post_status && !current_user_can( $post_type_object->cap->publish_posts ) ) )
+if ( ! ( $post_status_object->moderation && ! current_user_can( $post_type_object->cap->publish_posts ) ) )
 	add_meta_box('slugdiv', __('Slug'), 'post_slug_meta_box', $post_type, 'normal', 'core');
 
 if ( post_type_supports($post_type, 'author') ) {
@@ -247,7 +252,7 @@
 if ( !empty($shortlink) )
     $sample_permalink_html .= '<input id="shortlink" type="hidden" value="' . esc_attr($shortlink) . '" /><a href="#" class="button" onclick="prompt(&#39;URL:&#39;, jQuery(\'#shortlink\').val()); return false;">' . __('Get Shortlink') . '</a>';
 
-if ( !( 'pending' == $post->post_status && !current_user_can( $post_type_object->cap->publish_posts ) ) ) { ?>
+if ( ! ( $post_status_object->moderation && ! current_user_can( $post_type_object->cap->publish_posts ) ) ) { ?>
 	<div id="edit-slug-box">
 	<?php
 		if ( ! empty($post->ID) && ! empty($sample_permalink_html) && 'auto-draft' != $post->post_status )
@@ -314,3 +319,119 @@
 try{document.post.title.focus();}catch(e){}
 </script>
 <?php endif; ?>
+
+
+<?php
+add_action( 'admin_print_footer_scripts', create_function('', "_edit_form_advanced_scripts('$post_type');") );
+
+function _edit_form_advanced_scripts( $post_type ) {
+?>
+	
+<script type="text/javascript">
+//<![CDATA[
+jQuery(document).ready( function($) {
+	function updateStatusCaptions() {
+		postStatus = $('#post_status');
+
+		$('#post-status-display').html($('option:selected', postStatus).text());
+
+		switch( $('option:selected', postStatus).val() ) {
+
+<?php foreach( get_post_stati( array( 'public' => true, 'private' => true ), 'object', 'or' ) as $_status => $_status_obj ): ?>
+			case '<?php echo $_status ?>':
+				$('#save-post').hide();
+				break;
+<?php endforeach; ?>
+
+<?php foreach( get_post_stati( array( 'moderation' => true, 'internal' => false, 'object_type' => $post_type ), 'object' ) as $_status => $_status_obj ): ?>
+			case '<?php echo $_status ?>':
+				$('#save-post').show().val( '<?php echo $_status_obj->labels->save_as ?>' );
+				break;
+<?php endforeach; ?>
+
+			default :
+				<?php $draft_status_obj = get_post_status_object( 'draft' ); ?>			
+		 		$('#save-post').show().val( '<?php echo $draft_status_obj->labels->save_as ?>' );
+	 	}
+	}
+
+	$('input:radio', '#post-visibility-select').change(function() {
+		switch ( $('input:radio:checked', '#post-visibility-select').val() ) {
+			case 'public':
+				$('#sticky-span').show();
+				break;
+
+<?php foreach( get_post_stati( array( 'public' => true, 'object_type' => $post_type ) ) as $_status ) : ?>
+			case '<?php echo $_status ?>':
+				$('#sticky-span').show();
+				break;
+<?php endforeach; ?>
+
+			default :
+				$('#sticky').attr('checked', false);
+				$('#sticky-span').hide();
+		}
+	});
+	
+	function updateVisibilityCaptions() {
+		var postStatus = $('#post_status'), optPublish = $('option[value=publish]', postStatus);
+
+		switch( $('input:radio:checked', '#post-visibility-select').val() ) {
+
+<?php foreach( get_post_stati( array( 'internal' => false, 'object_type' => $post_type ), 'object' ) as $_status => $_status_obj ) : 
+		if ( ( 'publish' == $_status ) || ( ! $_status_obj->private && ! $_status_obj->public ) )
+			continue;
+?>
+			case '<?php echo $_status ?>':
+				$('#publish').val( postL10n.update );
+
+				if ( optPublish.length == 0 ) {
+					postStatus.append('<option value="publish">' + '<?php echo $_status_obj->labels->caption ?>' + '</option>');
+				} else {
+					optPublish.html( '<?php echo $_status_obj->labels->caption ?>' );
+				}
+
+				$('option[value=publish]', postStatus).attr('selected', true);
+				$('.edit-post-status', '#misc-publishing-actions').hide();
+
+				break;
+<?php endforeach; ?>
+
+			default:
+				if ( $('#original_post_status').val() == 'future' || $('#original_post_status').val() == 'draft' ) {
+					if ( optPublish.length ) {
+						optPublish.remove();
+						postStatus.val($('#hidden_post_status').val());
+					}
+				} else {
+					optPublish.html( postL10n.published );
+				}
+				if ( postStatus.is(':hidden') )
+					$('.edit-post-status', '#misc-publishing-actions').show();
+		}
+
+		return true;
+	}
+	
+	$('.save-post-status', '#post-status-select').click(function() {
+		updateStatusCaptions();
+		return false;
+	});
+	
+	$('.cancel-post-visibility', '#post-visibility-select').click(function () {
+		updateVisibilityCaptions();
+		return false;
+	});
+
+	$('.save-post-visibility', '#post-visibility-select').click(function () { // crazyhorse - multiple ok cancels
+		updateVisibilityCaptions();
+		updateStatusCaptions();
+		return false;
+	});
+});
+//]]>
+</script>
+
+<?php
+} // end function edit_form_advanced_scripts
+?>
\ No newline at end of file
Index: wp-admin/includes/default-list-tables.php
===================================================================
--- wp-admin/includes/default-list-tables.php	(revision 15818)
+++ wp-admin/includes/default-list-tables.php	(working copy)
@@ -137,15 +137,15 @@
 		}
 
 		$total_posts = array_sum( (array) $num_posts );
-
+		
 		// Subtract post types that are not included in the admin all list.
-		foreach ( get_post_stati( array('show_in_admin_all_list' => false) ) as $state )
+		foreach ( get_post_stati( array('show_in_admin_all_list' => false, 'object_type' => $post_type) ) as $state )
 			$total_posts -= $num_posts->$state;
 
 		$class = empty($class) && empty($_REQUEST['post_status']) ? ' class="current"' : '';
 		$status_links['all'] = "<li><a href='edit.php?post_type=$post_type{$allposts}'$class>" . sprintf( _nx( 'All <span class="count">(%s)</span>', 'All <span class="count">(%s)</span>', $total_posts, 'posts' ), number_format_i18n( $total_posts ) ) . '</a>';
 
-		foreach ( get_post_stati(array('show_in_admin_status_list' => true), 'objects') as $status ) {
+		foreach ( get_post_stati(array('show_in_admin_status_list' => true, 'object_type' => $post_type), 'objects') as $status ) {
 			$class = '';
 
 			$status_name = $status->name;
@@ -255,7 +255,10 @@
 		if ( empty( $post_type ) || is_object_in_taxonomy( $post_type, 'post_tag' ) )
 			$posts_columns['tags'] = __( 'Tags' );
 		$post_status = !empty( $_REQUEST['post_status'] ) ? $_REQUEST['post_status'] : 'all';
-		if ( !in_array( $post_status, array( 'pending', 'draft', 'future' ) ) && ( empty( $post_type ) || post_type_supports( $post_type, 'comments' ) ) )
+		
+		$post_status_obj = get_post_status_object( $post_status );
+
+		if ( ! in_array( $post_status, array( 'pending', 'draft', 'future' ) ) && empty( $GLOBALS['wp_post_statuses'][$post_status]->moderation ) && ( empty( $post_type ) || post_type_supports( $post_type, 'comments' ) ) )
 			$posts_columns['comments'] = '<div class="vers"><img alt="Comments" src="' . esc_url( admin_url( 'images/comment-grey-bubble.png' ) ) . '" /></div>';
 		$posts_columns['date'] = __( 'Date' );
 
@@ -523,7 +526,7 @@
 					if ( 'trash' == $post->post_status || !EMPTY_TRASH_DAYS )
 						$actions['delete'] = "<a class='submitdelete' title='" . esc_attr( __( 'Delete this item permanently' ) ) . "' href='" . get_delete_post_link( $post->ID, '', true ) . "'>" . __( 'Delete Permanently' ) . "</a>";
 				}
-				if ( in_array( $post->post_status, array( 'pending', 'draft' ) ) ) {
+				if ( in_array( $post->post_status, array( 'pending', 'draft' ) ) || ! empty( $GLOBALS['wp_post_statuses'][$post->post_status]->moderation ) ) {
 					if ( $can_edit_post )
 						$actions['view'] = '<a href="' . esc_url( add_query_arg( 'preview', 'true', get_permalink( $post->ID ) ) ) . '" title="' . esc_attr( sprintf( __( 'Preview &#8220;%s&#8221;' ), $title ) ) . '" rel="permalink">' . __( 'Preview' ) . '</a>';
 				} elseif ( 'trash' != $post->post_status ) {
@@ -559,7 +562,9 @@
 				else
 					echo '<abbr title="' . $t_time . '">' . apply_filters( 'post_date_column_time', $h_time, $post, $column_name, $mode ) . '</abbr>';
 				echo '<br />';
-				if ( 'publish' == $post->post_status ) {
+
+				$published_stati = get_post_stati( array( 'public' => true, 'private' => true ), 'names', 'or' );
+				if ( in_array( $post->post_status, $published_stati ) ) {
 					_e( 'Published' );
 				} elseif ( 'future' == $post->post_status ) {
 					if ( $time_diff > 0 )
@@ -761,17 +766,35 @@
 					<span class="title"><?php _e( 'Password' ); ?></span>
 					<span class="input-text-wrap"><input type="text" name="post_password" class="inline-edit-password-input" value="" /></span>
 				</label>
+				
+	<?php 
+	foreach ( get_post_stati( array( 'internal' => false, 'object_type' => $screen->post_type ), 'object' ) as $_status => $_status_obj ) :
+		if ( ( 'publish' == $_status ) || ( ! $_status_obj->private && ! $_status_obj->public ) ) // private and custom public stati only here
+			continue;
 
+		$set_status_cap = "set_{$_status}_posts";
+		$check_cap = ( ! empty( $post_type_object->cap->$set_status_cap ) ) ? $post_type_object->cap->$set_status_cap : $post_type_object->cap->publish_posts;
+
+		if ( current_user_can( $check_cap ) ) {
+			if ( empty( $did_or ) ) : 
+				$did_or = true;
+			?>
 				<em style="margin:5px 10px 0 0" class="alignleft">
 					<?php
 					/* translators: Between password field and private checkbox on post quick edit interface */
 					echo __( '&ndash;OR&ndash;' );
 					?>
 				</em>
-				<label class="alignleft inline-edit-private">
-					<input type="checkbox" name="keep_private" value="private" />
-					<span class="checkbox-title"><?php echo __( 'Private' ); ?></span>
-				</label>
+			<?php endif; ?>
+
+			<label class="alignleft keep-private inline-edit-<?php echo $_status ?>">
+				<input type="checkbox" name="keep_<?php echo $_status ?>" value="<?php echo $_status ?>" />
+				<span class="checkbox-title"><?php echo $_status_obj->labels->name ?>&nbsp; &nbsp;</span>
+			</label>			
+	<?php 
+		} // endif user can set status
+	endforeach; ?>
+	
 			</div>
 
 	<?php endif; ?>
@@ -908,14 +931,25 @@
 	<?php if ( $bulk ) : ?>
 						<option value="-1"><?php _e( '&mdash; No Change &mdash;' ); ?></option>
 	<?php endif; // $bulk ?>
-					<?php if ( $can_publish ) : // Contributors only get "Unpublished" and "Pending Review" ?>
+	<?php if ( $can_publish ) : // Contributors only get draft and moderation stati ?>
 						<option value="publish"><?php _e( 'Published' ); ?></option>
 						<option value="future"><?php _e( 'Scheduled' ); ?></option>
-	<?php if ( $bulk ) : ?>
-						<option value="private"><?php _e( 'Private' ) ?></option>
-	<?php endif; // $bulk ?>
-					<?php endif; ?>
-						<option value="pending"><?php _e( 'Pending Review' ); ?></option>
+	<?php if ( $bulk ) : 
+	foreach( get_post_stati( array( 'private' => true, 'object_type' => $screen->post_type ), 'object' ) as $_status => $_status_obj ) {
+	?>
+						<option value="<?php echo $_status ?>"><?php echo $_status_obj->labels->name ?></option>
+	<?php 
+	} // endforeach stati
+	endif; // $bulk ?>
+				<?php endif; ?>
+	<?php foreach( get_post_stati( array( 'moderation' => true, 'internal' => false, 'object_type' => $screen->post_type ), 'object' ) as $_status => $_status_obj ) :
+	$cap_name = "set_{$_status}_posts";
+	if ( empty( $post_type_object->cap->$cap_name ) || current_user_can( $post_type_object->cap->$cap_name ) ) {
+	?>
+						<option value="<?php echo $_status ?>"><?php echo $_status_obj->labels->caption ?></option>
+	<?php
+	} // endif
+	endforeach; ?>
 						<option value="draft"><?php _e( 'Draft' ); ?></option>
 					</select>
 				</label>
Index: wp-admin/includes/meta-boxes.php
===================================================================
--- wp-admin/includes/meta-boxes.php	(revision 15818)
+++ wp-admin/includes/meta-boxes.php	(working copy)
@@ -11,10 +11,22 @@
  */
 function post_submit_meta_box($post) {
 	global $action;
+	
+	$post_type = $post->post_type;
+	$post_type_obj = get_post_type_object($post_type);
+	$can_publish = current_user_can($post_type_obj->cap->publish_posts);
+	$post_status = $post->post_status;
+	
+	if ( ! $post_status_obj = get_post_status_object($post_status) )
+		$post_status_obj = get_post_status_object( 'draft' );
+	
+	$moderation_stati = get_post_stati( array( 'moderation' => true, 'internal' => false, 'object_type' => $post_type ), 'object' );
 
-	$post_type = $post->post_type;
-	$post_type_object = get_post_type_object($post_type);
-	$can_publish = current_user_can($post_type_object->cap->publish_posts);
+	foreach( array_keys($moderation_stati) as $_status ) { 
+		$set_cap = "set_{$_status}_posts";
+		if ( ( $_status != $post_status ) && ( ! empty( $post_type_obj->cap->$set_cap ) && ! current_user_can( $post_type_obj->cap->$set_cap ) ) )
+			unset( $moderation_stati[$_status] );
+	}
 ?>
 <div class="submitbox" id="submitpost">
 
@@ -27,17 +39,22 @@
 
 <div id="minor-publishing-actions">
 <div id="save-action">
-<?php if ( 'publish' != $post->post_status && 'future' != $post->post_status && 'pending' != $post->post_status )  { ?>
-<input <?php if ( 'private' == $post->post_status ) { ?>style="display:none"<?php } ?> type="submit" name="save" id="save-post" value="<?php esc_attr_e('Save Draft'); ?>" tabindex="4" class="button button-highlighted" />
-<?php } elseif ( 'pending' == $post->post_status && $can_publish ) { ?>
-<input type="submit" name="save" id="save-post" value="<?php esc_attr_e('Save as Pending'); ?>" tabindex="4" class="button button-highlighted" />
+<?php //if ( 'publish' != $post_status && 'future' != $post_status && 'pending' != $post_status )  { 
+if ( ! $post_status_obj->public && ! $post_status_obj->private && ! $post_status_obj->moderation && ( 'future' != $post_status ) ) {  // TODO: confirm we don't need a hidden save button when current status is private
+	$draft_status_obj = get_post_status_object( 'draft' );
+?>
+<input type="submit" name="save" id="save-post" value="<?php echo $draft_status_obj->labels->save_as ?>" tabindex="4" class="button button-highlighted" />
+<?php
+} elseif ( $post_status_obj->moderation && $can_publish ) { 			
+?>
+<input type="submit" name="save" id="save-post" value="<?php echo $post_status_obj->labels->save_as ?>" tabindex="4" class="button button-highlighted" />
 <?php } ?>
 <img src="<?php echo esc_url( admin_url( 'images/wpspin_light.gif' ) ); ?>" class="ajax-loading" id="draft-ajax-loading" alt="" />
 </div>
 
 <div id="preview-action">
 <?php
-if ( 'publish' == $post->post_status ) {
+if ( $post_status_obj->public ) {
 	$preview_link = esc_url(get_permalink($post->ID));
 	$preview_button = __('Preview Changes');
 } else {
@@ -57,48 +74,34 @@
 <div class="misc-pub-section<?php if ( !$can_publish ) { echo ' misc-pub-section-last'; } ?>"><label for="post_status"><?php _e('Status:') ?></label>
 <span id="post-status-display">
 <?php
-switch ( $post->post_status ) {
-	case 'private':
-		_e('Privately Published');
-		break;
-	case 'publish':
-		_e('Published');
-		break;
-	case 'future':
-		_e('Scheduled');
-		break;
-	case 'pending':
-		_e('Pending Review');
-		break;
-	case 'draft':
-	case 'auto-draft':
-		_e('Draft');
-		break;
-	case 'auto-draft':
-		_e('Unsaved');
-		break;
-}
+echo $post_status_obj->labels->caption;
 ?>
 </span>
-<?php if ( 'publish' == $post->post_status || 'private' == $post->post_status || $can_publish ) { ?>
-<a href="#post_status" <?php if ( 'private' == $post->post_status ) { ?>style="display:none;" <?php } ?>class="edit-post-status hide-if-no-js" tabindex='4'><?php _e('Edit') ?></a>
+<?php 
+$select_moderation = ( count($moderation_stati) > 1 || ( $post_status != key($moderation_stati) ) );  // multiple moderation stati are selectable or a single non-current moderation stati is selectable
 
+if ( $post_status_obj->public || $post_status_obj->private || $can_publish || $select_moderation ) { ?>
+<a href="#post_status" <?php if ( $post_status_obj->private || ( $post_status_obj->public && 'publish' != $post_status ) ) { ?>style="display:none;" <?php } ?>class="edit-post-status hide-if-no-js" tabindex='4'><?php _e('Edit') ?></a>
+
 <div id="post-status-select" class="hide-if-js">
-<input type="hidden" name="hidden_post_status" id="hidden_post_status" value="<?php echo esc_attr( ('auto-draft' == $post->post_status ) ? 'draft' : $post->post_status); ?>" />
+<input type="hidden" name="hidden_post_status" id="hidden_post_status" value="<?php echo esc_attr( ('auto-draft' == $post_status ) ? 'draft' : $post_status); ?>" />
 <select name='post_status' id='post_status' tabindex='4'>
-<?php if ( 'publish' == $post->post_status ) : ?>
-<option<?php selected( $post->post_status, 'publish' ); ?> value='publish'><?php _e('Published') ?></option>
-<?php elseif ( 'private' == $post->post_status ) : ?>
-<option<?php selected( $post->post_status, 'private' ); ?> value='publish'><?php _e('Privately Published') ?></option>
-<?php elseif ( 'future' == $post->post_status ) : ?>
-<option<?php selected( $post->post_status, 'future' ); ?> value='future'><?php _e('Scheduled') ?></option>
+
+<?php if ( $post_status_obj->public || $post_status_obj->private || ( 'future' == $post_status ) ) : ?>
+<option<?php selected( $post_status, $post_status ); ?> value='publish'><?php echo $post_status_obj->labels->caption ?></option>
 <?php endif; ?>
-<option<?php selected( $post->post_status, 'pending' ); ?> value='pending'><?php _e('Pending Review') ?></option>
-<?php if ( 'auto-draft' == $post->post_status ) : ?>
-<option<?php selected( $post->post_status, 'auto-draft' ); ?> value='draft'><?php _e('Draft') ?></option>
-<?php else : ?>
-<option<?php selected( $post->post_status, 'draft' ); ?> value='draft'><?php _e('Draft') ?></option>
-<?php endif; ?>
+
+<?php 
+foreach( $moderation_stati as $_status => $_status_obj ) : ?>
+<option<?php selected( $post_status, $_status ); ?> value='<?php echo $_status ?>'><?php echo $_status_obj->labels->caption ?></option>
+<?php endforeach ?>
+
+<?php 
+$draft_status_obj = get_post_status_object( 'draft' );
+$compare_status = ( 'auto-draft' == $post_status ) ? 'auto-draft' : 'draft';
+?>
+<option<?php selected( $post_status, $compare_status ); ?> value='draft'><?php echo $draft_status_obj->label ?></option>
+
 </select>
  <a href="#post_status" class="save-post-status hide-if-no-js button"><?php _e('OK'); ?></a>
  <a href="#post_status" class="cancel-post-status hide-if-no-js"><?php _e('Cancel'); ?></a>
@@ -110,16 +113,40 @@
 <div class="misc-pub-section " id="visibility">
 <?php _e('Visibility:'); ?> <span id="post-visibility-display"><?php
 
-if ( 'private' == $post->post_status ) {
+if ( 'future' == $post_status ) {	// indicate eventual visibility of scheduled post
+	if ( ! $vis_status = get_post_meta( $post->ID, '_scheduled_status', true ) )
+		$vis_status = 'publish';	
+
+	$vis_status_obj = get_post_status_object( $vis_status );
+} else {
+	$vis_status = $post_status;
+	$vis_status_obj = $post_status_obj;
+}
+	
+if ( 'publish' == $vis_status ) {
 	$post->post_password = '';
-	$visibility = 'private';
-	$visibility_trans = __('Private');
+	$visibility = 'public';
+
+	if ( post_type_supports( $post->post_type, 'sticky' ) && is_sticky( $post->ID ) ) {
+		$visibility_trans = __('Public, Sticky');
+	} else {
+	    $visibility_trans = __('Public');
+	}
+} elseif ( $vis_status_obj->public  ) {
+	$post->post_password = '';
+	$visibility = $vis_status;
+
+	if ( post_type_supports( $post->post_type, 'sticky' ) && is_sticky( $post->ID ) ) {
+		$visibility_trans = sprintf( __('%s, Sticky'), $vis_status_obj->label );
+    } else {
+	    $visibility_trans = $vis_status_obj->labels->visibility;
+	}
+} elseif ( $vis_status_obj->private ) {
+	$post->post_password = '';
+	$visibility_trans = $vis_status_obj->labels->visibility;
 } elseif ( !empty( $post->post_password ) ) {
 	$visibility = 'password';
 	$visibility_trans = __('Password protected');
-} elseif ( post_type_supports( $post->post_type, 'sticky' ) && is_sticky( $post->ID ) ) {
-	$visibility = 'public';
-	$visibility_trans = __('Public, Sticky');
 } else {
 	$visibility = 'public';
 	$visibility_trans = __('Public');
@@ -136,15 +163,37 @@
 <?php endif; ?>
 <input type="hidden" name="hidden_post_visibility" id="hidden-post-visibility" value="<?php echo esc_attr( $visibility ); ?>" />
 
+<input type="radio" name="visibility" id="visibility-radio-public" value="public" <?php checked( $visibility, 'public' ); ?> /> <label for="visibility-radio-public" class="selectit"><?php _e('Public'); ?></label><br />
 
-<input type="radio" name="visibility" id="visibility-radio-public" value="public" <?php checked( $visibility, 'public' ); ?> /> <label for="visibility-radio-public" class="selectit"><?php _e('Public'); ?></label><br />
-<?php if ( post_type_supports( $post->post_type, 'sticky' ) ): ?>
+<?php
+foreach( get_post_stati( array( 'public' => true, 'object_type' => $post_type ), 'object' ) as $_status => $_status_obj ) :
+	if ( 'publish' == $_status )
+		continue;
+
+	$post_cap = "set_{$_status}_posts";
+	if ( empty( $post_type_obj->cap->$post_cap ) || current_user_can( $post_type_obj->cap->$post_cap ) ) {
+?>
+<input type="radio" name="visibility" id="visibility-radio-<?php echo $_status ?>" value="<?php echo $_status ?>" <?php checked( $visibility, $_status ); ?> /> <label for="visibility-radio-<?php echo $_status ?>" class="selectit"><?php echo $_status_obj->labels->visibility ?></label><br />	
+<?php 
+	} // end if this user can set status
+endforeach ?>
+
+<?php if ( post_type_supports( $post->post_type, 'sticky' ) ) : ?>
 <span id="sticky-span"><input id="sticky" name="sticky" type="checkbox" value="sticky" <?php checked(is_sticky($post->ID)); ?> tabindex="4" /> <label for="sticky" class="selectit"><?php _e('Stick this to the front page') ?></label><br /></span>
 <?php endif; ?>
+
 <input type="radio" name="visibility" id="visibility-radio-password" value="password" <?php checked( $visibility, 'password' ); ?> /> <label for="visibility-radio-password" class="selectit"><?php _e('Password protected'); ?></label><br />
 <span id="password-span"><label for="post_password"><?php _e('Password:'); ?></label> <input type="text" name="post_password" id="post_password" value="<?php echo esc_attr($post->post_password); ?>" /><br /></span>
-<input type="radio" name="visibility" id="visibility-radio-private" value="private" <?php checked( $visibility, 'private' ); ?> /> <label for="visibility-radio-private" class="selectit"><?php _e('Private'); ?></label><br />
 
+<?php
+foreach( get_post_stati( array( 'private' => true, 'object_type' => $post_type ), 'object' ) as $_status => $_status_obj ) :
+	$post_cap = "set_{$_status}_posts";
+	if ( empty( $post_type_obj->cap->$post_cap ) || current_user_can( $post_type_obj->cap->$post_cap ) ) {
+?>
+<input type="radio" name="visibility" id="visibility-radio-<?php echo $_status ?>" value="<?php echo $_status ?>" <?php checked( $visibility, $_status ); ?> /> <label for="visibility-radio-<?php echo $_status ?>" class="selectit"><?php echo $_status_obj->label ?></label><br />	
+<?php 
+	} // end if this user can set status
+endforeach ?>
 <p>
  <a href="#visibility" class="save-post-visibility hide-if-no-js button"><?php _e('OK'); ?></a>
  <a href="#visibility" class="cancel-post-visibility hide-if-no-js"><?php _e('Cancel'); ?></a>
@@ -158,10 +207,13 @@
 <?php
 // translators: Publish box date formt, see http://php.net/date
 $datef = __( 'M j, Y @ G:i' );
+
+$published_stati = get_post_stati( array( 'public' => true, 'private' => true ), 'names', 'or' );
+
 if ( 0 != $post->ID ) {
-	if ( 'future' == $post->post_status ) { // scheduled for publishing at a future date
+	if ( 'future' == $post_status ) { // scheduled for publishing at a future date
 		$stamp = __('Scheduled for: <b>%1$s</b>');
-	} else if ( 'publish' == $post->post_status || 'private' == $post->post_status ) { // already published
+	} else if ( in_array( $post_status, $published_stati ) ) { // already published
 		$stamp = __('Published on: <b>%1$s</b>');
 	} else if ( '0000-00-00 00:00:00' == $post->post_date_gmt ) { // draft, 1 or more saves, no date specified
 		$stamp = __('Publish <b>immediately</b>');
@@ -207,18 +259,31 @@
 <div id="publishing-action">
 <img src="<?php echo esc_url( admin_url( 'images/wpspin_light.gif' ) ); ?>" class="ajax-loading" id="ajax-loading" alt="" />
 <?php
-if ( !in_array( $post->post_status, array('publish', 'future', 'private') ) || 0 == $post->ID ) {
+if ( ( ! $post_status_obj->public && ! $post_status_obj->private && ( 'future' != $post_status ) ) ) {
 	if ( $can_publish ) :
-		if ( !empty($post->post_date_gmt) && time() < strtotime( $post->post_date_gmt . ' +0000' ) ) : ?>
-		<input name="original_publish" type="hidden" id="original_publish" value="<?php esc_attr_e('Schedule') ?>" />
-		<input name="publish" type="submit" class="button-primary" id="publish" tabindex="5" accesskey="p" value="<?php esc_attr_e('Schedule') ?>" />
-<?php	else : ?>
-		<input name="original_publish" type="hidden" id="original_publish" value="<?php esc_attr_e('Publish') ?>" />
-		<input name="publish" type="submit" class="button-primary" id="publish" tabindex="5" accesskey="p" value="<?php esc_attr_e('Publish') ?>" />
+		if ( !empty($post->post_date_gmt) && time() < strtotime( $post->post_date_gmt . ' +0000' ) ) : 
+		$future_status_obj = get_post_status_object( 'future' );
+		?>
+		<input name="original_publish" type="hidden" id="original_publish" value="<?php echo $future_status_obj->labels->publish ?>" />
+		<input name="publish" type="submit" class="button-primary" id="publish" tabindex="5" accesskey="p" value="<?php echo $future_status_obj->labels->publish ?>" />
+<?php	else : 
+		$publish_status_obj = get_post_status_object( 'publish' );
+		?>
+		<input name="original_publish" type="hidden" id="original_publish" value="<?php echo $publish_status_obj->labels->publish ?>" />
+		<input name="publish" type="submit" class="button-primary" id="publish" tabindex="5" accesskey="p" value="<?php echo $publish_status_obj->labels->publish ?>" />
 <?php	endif;
 	else : ?>
-		<input name="original_publish" type="hidden" id="original_publish" value="<?php esc_attr_e('Submit for Review') ?>" />
-		<input name="publish" type="submit" class="button-primary" id="publish" tabindex="5" accesskey="p" value="<?php esc_attr_e('Submit for Review') ?>" />
+		<?php
+		$moderation_button_status = apply_filters( 'post_moderation_status', 'pending', $post->ID );
+
+		$status_obj = get_post_status_object( $moderation_button_status );
+		$cap_name = "set_{$moderation_button_status}_posts";
+
+		if ( ! $status_obj || ( ! empty($post_type_obj->cap->$cap_name) && ! current_user_can($post_type_obj->cap->$cap_name) ) )
+			$status_obj = get_post_status_object( 'pending' );
+		?>
+		<input name="original_publish" type="hidden" id="original_publish" value="<?php echo $status_obj->labels->publish ?>" />
+		<input name="publish" type="submit" class="button-primary" id="publish" tabindex="5" accesskey="p" value="<?php echo $status_obj->labels->publish ?>" />
 <?php
 	endif;
 } else { ?>
Index: wp-admin/includes/nav-menu.php
===================================================================
--- wp-admin/includes/nav-menu.php	(revision 15818)
+++ wp-admin/includes/nav-menu.php	(working copy)
@@ -1013,17 +1013,19 @@
  */
 function _wp_nav_menu_meta_box_object( $object = null ) {
 	if ( isset( $object->name ) ) {
+		$public_stati = apply_filters( 'nav_menu_metabox_stati', get_post_stati( array( 'public' => true, 'object_type' => $object->name ) ), $object );
+		$public_csv = implode( ',', $public_stati );
 
 		if ( 'page' == $object->name ) {
 			$object->_default_query = array(
 				'orderby' => 'menu_order title',
-				'post_status' => 'publish',
+				'post_status' => $public_csv,
 			);
 
-		// posts should show only published items
+		// posts should show only public items
 		} elseif ( 'post' == $object->name ) {
 			$object->_default_query = array(
-				'post_status' => 'publish',
+				'post_status' => $public_csv,
 			);
 
 		// cats should be in reverse chronological order
@@ -1033,10 +1035,10 @@
 				'order' => 'DESC',
 			);
 
-		// custom post types should show only published items
+		// custom post types should show only public items
 		} else {
 			$object->_default_query = array(
-				'post_status' => 'publish',
+				'post_status' => $public_csv,
 			);
 		}
 	}
Index: wp-admin/includes/post.php
===================================================================
--- wp-admin/includes/post.php	(revision 15818)
+++ wp-admin/includes/post.php	(working copy)
@@ -64,7 +64,7 @@
 	// What to do based on which button they pressed
 	if ( isset($post_data['saveasdraft']) && '' != $post_data['saveasdraft'] )
 		$post_data['post_status'] = 'draft';
-	if ( isset($post_data['saveasprivate']) && '' != $post_data['saveasprivate'] )
+	if ( isset($post_data['saveasprivate']) && '' != $post_data['saveasprivate'] ) // TODO: is this set anywhere?
 		$post_data['post_status'] = 'private';
 	if ( isset($post_data['publish']) && ( '' != $post_data['publish'] ) && ( !isset($post_data['post_status']) || $post_data['post_status'] != 'private' ) )
 		$post_data['post_status'] = 'publish';
@@ -81,10 +81,20 @@
 
 	// Posts 'submitted for approval' present are submitted to $_POST the same as if they were being published.
 	// Change status from 'publish' to 'pending' if user lacks permissions to publish or to resave published posts.
-	if ( isset($post_data['post_status']) && ('publish' == $post_data['post_status'] && !current_user_can( $ptype->cap->publish_posts )) )
-		if ( $previous_status != 'publish' || !current_user_can( 'edit_post', $post_id ) )
-			$post_data['post_status'] = 'pending';
+	if ( isset($post_data['post_status']) && ('publish' == $post_data['post_status'] && !current_user_can( $ptype->cap->publish_posts )) ) {
+		if ( $previous_status != 'publish' || !current_user_can( 'edit_post', $post_id ) ) {
+			$moderation_status = apply_filters( 'post_moderation_status', 'pending', $post->ID );
 
+			$_status_obj = get_post_status_object( $moderation_status );
+			$cap_name = "set_{$moderation_status}_posts";
+
+			if ( empty($_status_obj) || ! $_status_obj->moderation || ( ! empty($ptype->cap->$cap_name) && ! current_user_can($ptype->cap->$cap_name) ) )
+				$moderation_status = 'pending';
+
+			$post_data['post_status'] = $moderation_status;
+		}
+	}
+
 	if ( ! isset($post_data['post_status']) )
 		$post_data['post_status'] = $previous_status;
 
@@ -169,11 +179,17 @@
 			case 'password' :
 				unset( $post_data['sticky'] );
 				break;
-			case 'private' :
-				$post_data['post_status'] = 'private';
-				$post_data['post_password'] = '';
-				unset( $post_data['sticky'] );
-				break;
+			default:
+				$status_obj = get_post_status_object( $post_data['visibility'] );
+	
+				if ( ! empty( $status_obj->private ) || ! empty( $status_obj->public ) ) {
+					$post_data['post_status'] = $status_obj->name;
+					$post_data['post_password'] = '';
+				}
+
+				if ( ! empty( $status_obj->private ) ) {
+					unset( $post_data['sticky'] );
+				}
 		}
 	}
 
@@ -476,11 +492,13 @@
 			case 'password' :
 				unset( $_POST['sticky'] );
 				break;
-			case 'private' :
-				$_POST['post_status'] = 'private';
-				$_POST['post_password'] = '';
-				unset( $_POST['sticky'] );
-				break;
+			default:
+				$status_obj = get_post_status_object( $_POST['visibility'] );	
+				if ( ! empty( $status_obj->private ) ) {
+					$_POST['post_status'] = $status_obj->name;
+					$_POST['post_password'] = '';
+					unset( $_POST['sticky'] );
+				}
 		}
 	}
 
@@ -827,7 +845,7 @@
 
 	if ( isset($q['orderby']) )
 		$orderby = $q['orderby'];
-	elseif ( isset($q['post_status']) && in_array($q['post_status'], array('pending', 'draft')) )
+	elseif ( isset($q['post_status']) && ( in_array($q['post_status'], array('pending', 'draft')) || ! empty( $GLOBALS['wp_post_statuses'][$q['post_status']]->moderation ) ) )
 		$orderby = 'modified';
 
 	if ( isset($q['order']) )
@@ -977,9 +995,11 @@
 	$original_date = $post->post_date;
 	$original_name = $post->post_name;
 
+	$post_status_obj = get_post_status_object( $post->post_status );
+	
 	// Hack: get_permalink would return ugly permalink for
 	// drafts, so we will fake, that our post is published
-	if ( in_array($post->post_status, array('draft', 'pending')) ) {
+	if ( in_array($post->post_status, array('draft', 'pending')) || ! empty($post_status_obj->moderation) ) {
 		$post->post_status = 'publish';
 		$post->post_name = sanitize_title($post->post_name ? $post->post_name : $post->post_title, $post->ID);
 	}
@@ -1036,7 +1056,8 @@
 
 	list($permalink, $post_name) = get_sample_permalink($post->ID, $new_title, $new_slug);
 
-	if ( 'publish' == $post->post_status ) {
+	$public_stati = get_post_stati( array( 'public' => true ) );
+	if ( in_array( $post->post_status, $public_stati ) ) {
 		$ptype = get_post_type_object($post->post_type);
 		$view_post = $ptype->labels->view_item;
 		$title = __('Click to edit this part of the permalink');
Index: wp-admin/includes/template.php
===================================================================
--- wp-admin/includes/template.php	(revision 15818)
+++ wp-admin/includes/template.php	(working copy)
@@ -277,7 +277,16 @@
 	<div class="post_author">' . $post->post_author . '</div>
 	<div class="comment_status">' . $post->comment_status . '</div>
 	<div class="ping_status">' . $post->ping_status . '</div>
-	<div class="_status">' . $post->post_status . '</div>
+	<div class="_status">' . $post->post_status . '</div>';
+
+	if ( 'future' == $post->post_status ) {
+		if ( ! $scheduled_status = get_post_meta( $post->ID, '_scheduled_status', true ) )
+			$scheduled_status = 'publish';	
+	echo '
+	<div class="scheduled_status">' . $scheduled_status . '</div>';
+	}
+
+	echo '
 	<div class="jj">' . mysql2date( 'd', $post->post_date, false ) . '</div>
 	<div class="mm">' . mysql2date( 'm', $post->post_date, false ) . '</div>
 	<div class="aa">' . mysql2date( 'Y', $post->post_date, false ) . '</div>
@@ -579,9 +588,11 @@
 function touch_time( $edit = 1, $for_post = 1, $tab_index = 0, $multi = 0 ) {
 	global $wp_locale, $post, $comment;
 
-	if ( $for_post )
-		$edit = ! ( in_array($post->post_status, array('draft', 'pending') ) && (!$post->post_date_gmt || '0000-00-00 00:00:00' == $post->post_date_gmt ) );
-
+	if ( $for_post ) {
+		$post_status_obj = get_post_status_object($post->post_status);
+		$edit = ( ( 'draft' != $post->post_status ) && empty($post_status_obj->moderation) ) || ( $post->post_date_gmt && '0000-00-00 00:00:00' != $post->post_date_gmt );
+	}
+			
 	$tab_index_attribute = '';
 	if ( (int) $tab_index > 0 )
 		$tab_index_attribute = " tabindex=\"$tab_index\"";
@@ -1596,15 +1607,26 @@
 	else
 		$post_status = '';
 
+	$post_status_obj = get_post_status_object($post->post_status);
+	
 	if ( !empty($post->post_password) )
 		$post_states[] = __('Password protected');
-	if ( 'private' == $post->post_status && 'private' != $post_status )
-		$post_states[] = __('Private');
-	if ( 'draft' == $post->post_status && 'draft' != $post_status )
-		$post_states[] = __('Draft');
-	if ( 'pending' == $post->post_status && 'pending' != $post_status )
-		/* translators: post state */
-		$post_states[] = _x('Pending', 'post state');
+	
+	if ( $post_status_obj && ( 'publish' != $post->post_status ) ) {
+		if ( $post->post_status != $post_status ) {
+			$post_states[] = $post_status_obj->label;
+
+			if ( 'future' == $post->post_status ) {
+				if ( $scheduled_status = get_post_meta( $post->ID, '_scheduled_status', true ) ) {
+					if ( 'publish' != $scheduled_status ) {
+						if ( $_scheduled_status_obj = get_post_status_object( $scheduled_status ) )
+							$post_states[] = $_scheduled_status_obj->label;
+					}
+				}
+			}
+		}
+	}
+
 	if ( is_sticky($post->ID) )
 		$post_states[] = __('Sticky');
 
Index: wp-admin/js/inline-edit-post.dev.js
===================================================================
--- wp-admin/js/inline-edit-post.dev.js	(revision 15818)
+++ wp-admin/js/inline-edit-post.dev.js	(working copy)
@@ -78,6 +78,12 @@
 			if ( $('form#posts-filter tr.inline-editor').length > 0 )
 				t.revert();
 		});
+		
+		$('.keep-private, .keep-private input').click(function() {
+			if ( $(this).attr('checked') ) {
+				$('.keep-private input').filter("[name!='" + $(this).attr('name') + "']").removeAttr('checked');
+			}
+		});
 	},
 
 	toggle : function(el){
@@ -128,7 +134,7 @@
 		if ( typeof(id) == 'object' )
 			id = t.getId(id);
 
-		fields = ['post_title', 'post_name', 'post_author', '_status', 'jj', 'mm', 'aa', 'hh', 'mn', 'ss', 'post_password'];
+		fields = ['post_title', 'post_name', 'post_author', '_status', 'scheduled_status', 'jj', 'mm', 'aa', 'hh', 'mn', 'ss', 'post_password'];
 		if ( t.type == 'page' )
 			fields.push('post_parent', 'menu_order', 'page_template');
 
@@ -178,14 +184,17 @@
 			}
 		});
 
-
 		// handle the post status
 		status = $('._status', rowData).text();
-		if ( 'future' != status )
+
+		if ( 'future' == status ) {
+			status = $('.scheduled_status', rowData).text();
+		} else {
 			$('select[name="_status"] option[value="future"]', editRow).remove();
+		}
 
-		if ( 'private' == status ) {
-			$('input[name="keep_private"]', editRow).attr("checked", "checked");
+		if ( $('[name=keep_' + status + ']').length > 0) {
+			$('input[name="keep_' + status + '"]', editRow).attr("checked", "checked");
 			$('input.inline-edit-password-input').val('').attr('disabled', 'disabled');
 		}
 
Index: wp-admin/js/post.dev.js
===================================================================
--- wp-admin/js/post.dev.js	(revision 15818)
+++ wp-admin/js/post.dev.js	(working copy)
@@ -346,12 +346,6 @@
 
 		function updateVisibility() {
 			var pvSelect = $('#post-visibility-select');
-			if ( $('input:radio:checked', pvSelect).val() != 'public' ) {
-				$('#sticky').attr('checked', false);
-				$('#sticky-span').hide();
-			} else {
-				$('#sticky-span').show();
-			}
 			if ( $('input:radio:checked', pvSelect).val() != 'password' ) {
 				$('#password-span').hide();
 			} else {
@@ -360,7 +354,7 @@
 		}
 
 		function updateText() {
-			var attemptedDate, originalDate, currentDate, publishOn, page = 'page' == pagenow || 'page-new' == pagenow,
+			var attemptedDate, originalDate, currentDate, publishOn,
 				postStatus = $('#post_status'),	optPublish = $('option[value=publish]', postStatus), aa = $('#aa').val(),
 				mm = $('#mm').val(), jj = $('#jj').val(), hh = $('#hh').val(), mn = $('#mn').val();
 
@@ -383,11 +377,9 @@
 				$('#publish').val( postL10n.publish );
 			} else {
 				publishOn = postL10n.publishOnPast;
-				if ( page )
-					$('#publish').val( postL10n.updatePage );
-				else
-					$('#publish').val( postL10n.updatePost );
+				$('#publish').val( postL10n.update );
 			}
+
 			if ( originalDate.toUTCString() == attemptedDate.toUTCString() ) { //hack
 				$('#timestamp').html(stamp);
 			} else {
@@ -402,15 +394,10 @@
 			}
 
 			if ( $('input:radio:checked', '#post-visibility-select').val() == 'private' ) {
-				if ( page )
-					$('#publish').val( postL10n.updatePage );
-				else
-					$('#publish').val( postL10n.updatePost );
-				if ( optPublish.length == 0 ) {
-					postStatus.append('<option value="publish">' + postL10n.privatelyPublished + '</option>');
-				} else {
-					optPublish.html( postL10n.privatelyPublished );
+				if ( attemptedDate <= currentDate ) {
+					$('#publish').val( postL10n.update );
 				}
+
 				$('option[value=publish]', postStatus).attr('selected', true);
 				$('.edit-post-status', '#misc-publishing-actions').hide();
 			} else {
@@ -425,17 +412,7 @@
 				if ( postStatus.is(':hidden') )
 					$('.edit-post-status', '#misc-publishing-actions').show();
 			}
-			$('#post-status-display').html($('option:selected', postStatus).text());
-			if ( $('option:selected', postStatus).val() == 'private' || $('option:selected', postStatus).val() == 'publish' ) {
-				$('#save-post').hide();
-			} else {
-				$('#save-post').show();
-				if ( $('option:selected', postStatus).val() == 'pending' ) {
-					$('#save-post').show().val( postL10n.savePending );
-				} else {
-					$('#save-post').show().val( postL10n.saveDraft );
-				}
-			}
+
 			return true;
 		}
 
@@ -447,7 +424,7 @@
 			}
 			return false;
 		});
-
+		
 		$('.cancel-post-visibility', '#post-visibility-select').click(function () {
 			$('#post-visibility-select').slideUp("normal");
 			$('#visibility-radio-' + $('#hidden-post-visibility').val()).attr('checked', true);
@@ -455,31 +432,25 @@
 			$('#sticky').attr('checked', $('#hidden-post-sticky').attr('checked'));
 			$('#post-visibility-display').html(visibility);
 			$('.edit-visibility', '#visibility').show();
-			updateText();
 			return false;
 		});
-
+		
 		$('.save-post-visibility', '#post-visibility-select').click(function () { // crazyhorse - multiple ok cancels
 			var pvSelect = $('#post-visibility-select');
 
 			pvSelect.slideUp("normal");
 			$('.edit-visibility', '#visibility').show();
-			updateText();
 
-			if ( $('input:radio:checked', pvSelect).val() != 'public' ) {
-				$('#sticky').attr('checked', false);
-			} // WEAPON LOCKED
-
 			if ( true == $('#sticky').attr('checked') ) {
 				sticky = 'Sticky';
 			} else {
 				sticky = '';
 			}
-
+	
 			$('#post-visibility-display').html(	postL10n[$('input:radio:checked', pvSelect).val() + sticky]	);
 			return false;
 		});
-
+		
 		$('input:radio', '#post-visibility-select').change(function() {
 			updateVisibility();
 		});
Index: wp-admin/post.php
===================================================================
--- wp-admin/post.php	(revision 15818)
+++ wp-admin/post.php	(working copy)
@@ -68,7 +68,11 @@
 					$message = 9;
 					break;
 				default:
-					$message = 6;
+					$status_obj = get_post_status_object( $status );
+					if ( ! empty($status_obj->moderation) )
+						$message = 11;
+					else
+						$message = 6;
 			}
 		} else {
 				$message = 'draft' == $status ? 10 : 1;
Index: wp-comments-post.php
===================================================================
--- wp-comments-post.php	(revision 15818)
+++ wp-comments-post.php	(working copy)
@@ -37,7 +37,7 @@
 } elseif ( 'trash' == $status ) {
 	do_action('comment_on_trash', $comment_post_ID);
 	exit;
-} elseif ( !$status_obj->public && !$status_obj->private ) {
+} elseif ( empty($status_obj->public) && empty($status_obj->private) ) {
 	do_action('comment_on_draft', $comment_post_ID);
 	exit;
 } elseif ( post_password_required($comment_post_ID) ) {
Index: wp-includes/default-widgets.php
===================================================================
--- wp-includes/default-widgets.php	(revision 15818)
+++ wp-includes/default-widgets.php	(working copy)
@@ -542,7 +542,10 @@
 		else if ( $number > 15 )
 			$number = 15;
 
-		$r = new WP_Query(array('posts_per_page' => $number, 'nopaging' => 0, 'post_status' => 'publish', 'ignore_sticky_posts' => true));
+		$public_stati = apply_filters( 'recent_posts_stati', get_post_stati( array( 'public' => true ) ) );
+		$status_arg = implode( ',', $public_stati );
+
+		$r = new WP_Query(array('posts_per_page' => $number, 'nopaging' => 0, 'post_status' => $status_arg, 'ignore_sticky_posts' => true));
 		if ($r->have_posts()) :
 ?>
 		<?php echo $before_widget; ?>
Index: wp-includes/functions.php
===================================================================
--- wp-includes/functions.php	(revision 15818)
+++ wp-includes/functions.php	(working copy)
@@ -3047,8 +3047,18 @@
 	$filtered = array();
 
 	foreach ( $list as $key => $obj ) {
-		$matched = count( array_intersect_assoc( (array) $obj, $args ) );
-		if ( ('and' == $operator && $matched == $count) || ('or' == $operator && $matched <= $count) ) {
+		$matched = array_intersect_assoc( (array) $obj, $args );
+
+		foreach( array_keys( $matched ) as $_key ) {
+			if ( is_array( $args[$_key] ) ) {
+				if ( ! array_intersect( $args[$_key], $matched[$_key] ) )
+					unset( $matched[$_key] );	
+			}	
+		}
+
+		$num_matched = count( $matched );
+		
+		if ( ( 'and' == $operator && $num_matched == $count ) || ( 'or' == $operator && $num_matched ) ) {
 			$filtered[$key] = $obj;
 		}
 	}
Index: wp-includes/general-template.php
===================================================================
--- wp-includes/general-template.php	(revision 15818)
+++ wp-includes/general-template.php	(working copy)
@@ -882,7 +882,10 @@
 	}
 
 	//filters
-	$where = apply_filters('getarchives_where', "WHERE post_type = 'post' AND post_status = 'publish'", $r );
+	$public_stati = apply_filters( 'getarchives_stati', get_post_stati( array( 'public' => true ) ) );
+	$public_csv = implode( "', '", $public_stati );
+
+	$where = apply_filters('getarchives_where', "WHERE post_type = 'post' AND post_status IN ('" . $public_csv . "')", $r );
 	$join = apply_filters('getarchives_join', "", $r);
 
 	$output = '';
Index: wp-includes/link-template.php
===================================================================
--- wp-includes/link-template.php	(revision 15818)
+++ wp-includes/link-template.php	(working copy)
@@ -114,7 +114,7 @@
 
 	$permalink = apply_filters('pre_post_link', $permalink, $post, $leavename);
 
-	if ( '' != $permalink && !in_array($post->post_status, array('draft', 'pending', 'auto-draft')) ) {
+	if ( '' != $permalink && ! in_array( $post->post_status, array('draft', 'auto-draft', 'pending') ) && empty( $GLOBALS['wp_post_statuses'][$post->post_status]->moderation ) ) {
 		$unixtime = strtotime($post->post_date);
 
 		$category = '';
@@ -185,7 +185,7 @@
 
 	$slug = $post->post_name;
 
-	$draft_or_pending = in_array( $post->post_status, array( 'draft', 'pending', 'auto-draft' ) );
+	$draft_or_pending = in_array( $post->post_status, array('draft', 'auto-draft', 'pending') ) || ! empty( $GLOBALS['wp_post_statuses'][$post->post_status]->moderation );
 
 	$post_type = get_post_type_object($post->post_type);
 
@@ -273,7 +273,7 @@
 
 	$link = $wp_rewrite->get_page_permastruct();
 
-	if ( '' != $link && ( ( isset($post->post_status) && 'draft' != $post->post_status && 'pending' != $post->post_status ) || $sample ) ) {
+	if ( '' != $link && ( ( isset($post->post_status) && ! in_array( $post->post_status, array('draft', 'auto-draft', 'pending') ) && empty( $GLOBALS['wp_post_statuses'][$post->post_status]->moderation ) ) || $sample ) ) {
 		if ( ! $leavename )
 			$link = str_replace('%pagename%', get_page_uri($id), $link);
 		$link = home_url($link);
Index: wp-includes/nav-menu.php
===================================================================
--- wp-includes/nav-menu.php	(revision 15818)
+++ wp-includes/nav-menu.php	(working copy)
@@ -466,8 +466,11 @@
 	if ( empty( $items ) )
 		return $items;
 
+	$public_stati = apply_filters( 'nav_menu_stati', get_post_stati( array( 'public' => true ) ), $menu );
+	$public_csv = implode( ',', $public_stati );
+
 	$defaults = array( 'order' => 'ASC', 'orderby' => 'menu_order', 'post_type' => 'nav_menu_item',
-		'post_status' => 'publish', 'output' => ARRAY_A, 'output_key' => 'menu_order', 'nopaging' => true,
+		'post_status' => $public_csv, 'output' => ARRAY_A, 'output_key' => 'menu_order', 'nopaging' => true,
 		'update_post_term_cache' => false );
 	$args = wp_parse_args( $args, $defaults );
 	if ( count( $items ) > 1 )
Index: wp-includes/post-template.php
===================================================================
--- wp-includes/post-template.php	(revision 15818)
+++ wp-includes/post-template.php	(working copy)
@@ -108,12 +108,22 @@
 	$id = isset($post->ID) ? $post->ID : (int) $id;
 
 	if ( !is_admin() ) {
-		if ( !empty($post->post_password) ) {
-			$protected_title_format = apply_filters('protected_title_format', __('Protected: %s'));
+		if ( ! empty($post->post_password) ) {
+			$protected_title_format = apply_filters( 'protected_title_format', __( 'Protected: %s' ) );
 			$title = sprintf($protected_title_format, $title);
-		} else if ( isset($post->post_status) && 'private' == $post->post_status ) {
-			$private_title_format = apply_filters('private_title_format', __('Private: %s'));
-			$title = sprintf($private_title_format, $title);
+		} elseif ( isset($post->post_status) ) {
+			$post_status_obj = get_post_status_object( $post->post_status );
+			if ( $post_status_obj && $post_status_obj->private ) {
+				if ( 'private' == $post->post_status ) {
+					$format_string = __( 'Private: %s' );  // preserve existing translation string
+					$private_title_format = apply_filters( 'private_title_format', $format_string );
+					$title = sprintf($private_title_format, $title);
+				} else {
+					$format_string = _x( '%1$s: %2$s', 'post status: title' );	
+					$private_title_format = apply_filters( 'post_title_format', $format_string, $_status );
+					$title = sprintf($private_title_format, $post_status_obj->labels->name, $title);
+				}
+			}
 		}
 	}
 	return apply_filters( 'the_title', $title, $id );
@@ -631,7 +641,7 @@
 	if ( 1 == $i ) {
 		$url = get_permalink();
 	} else {
-		if ( '' == get_option('permalink_structure') || in_array($post->post_status, array('draft', 'pending')) )
+		if ( '' == get_option('permalink_structure') || in_array( $post->post_status, array('draft', 'auto-draft', 'pending') ) || ! empty( $GLOBALS['wp_post_statuses'][$post->post_status]->moderation )  )
 			$url = add_query_arg( 'page', $i, get_permalink() );
 		elseif ( 'page' == get_option('show_on_front') && get_option('page_on_front') == $post->ID )
 			$url = trailingslashit(get_permalink()) . user_trailingslashit("$wp_rewrite->pagination_base/" . $i, 'single_paged');
Index: wp-includes/post.php
===================================================================
--- wp-includes/post.php	(revision 15818)
+++ wp-includes/post.php	(working copy)
@@ -81,56 +81,80 @@
 	) );
 
 	register_post_status( 'publish', array(
-		'label'       => _x( 'Published', 'post' ),
+		'labels'	  => array( 
+			'name' 	  => _x( 'Published', 'post' ),
+			'publish' => esc_attr__('Publish'),
+			'count' => _n_noop( 'Published <span class="count">(%s)</span>', 'Published <span class="count">(%s)</span>' ),
+		),
 		'public'      => true,
 		'_builtin'    => true, /* internal use only. */
-		'label_count' => _n_noop( 'Published <span class="count">(%s)</span>', 'Published <span class="count">(%s)</span>' ),
 	) );
 
 	register_post_status( 'future', array(
-		'label'       => _x( 'Scheduled', 'post' ),
+		'labels'	  => array( 
+			'name' 	  => _x( 'Scheduled', 'post' ),
+			'publish' => esc_attr__('Schedule'),
+			'count' => _n_noop('Scheduled <span class="count">(%s)</span>', 'Scheduled <span class="count">(%s)</span>' ),
+		),
 		'protected'   => true,
 		'_builtin'    => true, /* internal use only. */
-		'label_count' => _n_noop('Scheduled <span class="count">(%s)</span>', 'Scheduled <span class="count">(%s)</span>' ),
 	) );
 
 	register_post_status( 'draft', array(
-		'label'       => _x( 'Draft', 'post' ),
+		'labels'	  => array( 
+			'name' 	  => _x( 'Draft', 'post' ),
+			'save_as' => esc_attr__('Save Draft'), 
+			'count' => _n_noop( 'Draft <span class="count">(%s)</span>', 'Drafts <span class="count">(%s)</span>' ),
+		),
 		'protected'   => true,
 		'_builtin'    => true, /* internal use only. */
-		'label_count' => _n_noop( 'Draft <span class="count">(%s)</span>', 'Drafts <span class="count">(%s)</span>' ),
 	) );
 
 	register_post_status( 'pending', array(
-		'label'       => _x( 'Pending', 'post' ),
+		'labels'	  => array( 
+			'name' 	  => _x( 'Pending', 'post' ),
+			'caption' => __( 'Pending Review' ),
+			'publish' => esc_attr__('Submit for Review'),
+			'count' => _n_noop( 'Pending <span class="count">(%s)</span>', 'Pending <span class="count">(%s)</span>' ),
+		),
 		'protected'   => true,
 		'_builtin'    => true, /* internal use only. */
-		'label_count' => _n_noop( 'Pending <span class="count">(%s)</span>', 'Pending <span class="count">(%s)</span>' ),
+		'moderation'  => true,
 	) );
 
 	register_post_status( 'private', array(
-		'label'       => _x( 'Private', 'post' ),
+		'labels'	  => array( 
+			'name'   => _x( 'Private', 'post' ),
+			'caption' => __( 'Privately Published' ),
+			'count' => _n_noop( 'Private <span class="count">(%s)</span>', 'Private <span class="count">(%s)</span>' ),
+		),
 		'private'     => true,
 		'_builtin'    => true, /* internal use only. */
-		'label_count' => _n_noop( 'Private <span class="count">(%s)</span>', 'Private <span class="count">(%s)</span>' ),
 	) );
 
 	register_post_status( 'trash', array(
-		'label'       => _x( 'Trash', 'post' ),
+		'labels'	  => array( 
+			'name'   => _x( 'Trash', 'post' ),
+			'count' => _n_noop( 'Trash <span class="count">(%s)</span>', 'Trash <span class="count">(%s)</span>' ),
+		),
 		'internal'    => true,
 		'_builtin'    => true, /* internal use only. */
-		'label_count' => _n_noop( 'Trash <span class="count">(%s)</span>', 'Trash <span class="count">(%s)</span>' ),
 		'show_in_admin_status_list' => true,
 	) );
 
 	register_post_status( 'auto-draft', array(
-		'label'    => 'auto-draft',
+		'labels'	  => array( 
+			'name'    => 'auto-draft',
+			'caption' => __( 'Draft' ),
+		),
 		'internal' => true,
 		'_builtin' => true, /* internal use only. */
 	) );
 
 	register_post_status( 'inherit', array(
-		'label'    => 'inherit',
+		'labels'	  => array( 
+			'name'   => 'inherit',
+		),
 		'internal' => true,
 		'_builtin' => true, /* internal use only. */
 		'exclude_from_search' => false,
@@ -594,6 +618,32 @@
 }
 
 /**
+ * Add an already registered post status to an object type.
+ *
+ * @package WordPress
+ * @subpackage Taxonomy
+ * @since 3.1.0
+ * @uses $wp_post_statuses Modifies post_type object
+ *
+ * @param string $post_status Name of post_status object
+ * @param array|string $object_type Name of the object type
+ * @return bool True if successful, false if not
+ */
+function register_status_for_object_type( $post_status, $object_type) {
+	global $wp_post_statuses;
+
+	if ( ! isset( $wp_post_statuses[$post_status] ) )
+		return false;
+
+	if ( ! get_post_type_object($object_type) )
+		return false;
+
+	$wp_post_statuses[$post_status]->object_type[] = $object_type;
+
+	return true;
+}
+
+/**
  * Register a post type. Do not use before init.
  *
  * A simple function for creating or modifying a post status based on the
@@ -622,13 +672,20 @@
 		$wp_post_statuses = array();
 
 	// Args prefixed with an underscore are reserved for internal use.
-	$defaults = array('label' => false, 'label_count' => false, 'exclude_from_search' => null, '_builtin' => false, '_edit_link' => 'post.php?post=%d', 'capability_type' => 'post', 'hierarchical' => false, 'public' => null, 'internal' => null, 'protected' => null, 'private' => null, 'show_in_admin_all' => null, 'publicly_queryable' => null, 'show_in_admin_status_list' => null, 'show_in_admin_all_list' => null, 'single_view_cap' => null);
+	$defaults = array( 'object_type' => null, 'labels' => array(), 'label_count' => false, 'exclude_from_search' => null, '_builtin' => false, '_edit_link' => 'post.php?post=%d', 'capability_type' => 'post', 'hierarchical' => false, 'public' => null, 'internal' => null, 'protected' => null, 'private' => null, 'moderation' => null, 'show_in_admin_all' => null, 'publicly_queryable' => null, 'show_in_admin_status_list' => null, 'show_in_admin_all_list' => null, 'single_view_cap' => null);
+	
 	$args = wp_parse_args($args, $defaults);
 	$args = (object) $args;
 
 	$post_status = sanitize_key($post_status);
 	$args->name = $post_status;
 
+	if ( null == $args->object_type ) {
+		$args->object_type = get_post_types( array( 'public' => true, 'show_ui' => true ) );
+	} else {
+		$args->object_type = (array) $args->object_type;
+	}
+	
 	if ( null === $args->public && null === $args->internal && null === $args->protected && null === $args->private )
 		$args->internal = true;
 
@@ -638,6 +695,9 @@
 	if ( null === $args->private  )
 		$args->private = false;
 
+	if ( null === $args->moderation  )
+		$args->moderation = false;
+		
 	if ( null === $args->protected  )
 		$args->protected = false;
 
@@ -659,12 +719,38 @@
 	if ( null === $args->single_view_cap )
 		$args->single_view_cap = $args->public ? '' : 'edit';
 
-	if ( false === $args->label )
-		$args->label = $post_status;
+	$args->labels = (object) $args->labels;
 
-	if ( false === $args->label_count )
-		$args->label_count = array( $args->label, $args->label );
+	if ( empty( $args->labels->name ) )
+		$args->labels->name = ( ! empty( $args->label ) ) ? $args->label : $post_status;
+	
+	if ( empty( $args->label ) )
+		$args->label = $args->labels->name;
+		
+	if ( empty( $args->labels->caption ) )
+		$args->labels->caption = $args->label;
 
+	if ( empty( $args->labels->count ) )
+		$args->labels->count = ( ! empty( $args->label_count ) ) ? $args->label_count : array( $args->label, $args->label );
+
+	if ( empty( $args->label_count ) )	// TODO: need to support this for external API?
+		$args->label_count = $args->labels->count;
+
+	if ( empty( $args->labels->publish ) )
+		$args->labels->publish = esc_attr( sprintf( __( 'Set %s' ), $args->label ) );
+
+	if ( empty( $args->labels->save_as ) )
+		$args->labels->save_as = esc_attr( sprintf( __( 'Save as %s' ), $args->label ) );
+
+	if ( empty( $args->labels->visibility ) ) {
+		if ( 'publish' == $post_status )
+			$args->labels->visibility =__( 'Public' );
+		elseif ( $args->public )
+			$args->labels->visibility = esc_attr( sprintf( __( 'Public (%s)' ), $args->label ) );
+		elseif ( $args->private )
+			$args->labels->visibility = $args->label;
+	}
+
 	$wp_post_statuses[$post_status] = $args;
 
 	return $args;
@@ -713,6 +799,9 @@
 
 	$field = ('names' == $output) ? 'name' : false;
 
+	if ( ! empty( $args['object_type'] ) )
+		$args['object_type'] = (array) $args['object_type'];
+	
 	return wp_filter_object_list($wp_post_statuses, $args, $operator, $field);
 }
 
@@ -868,7 +957,7 @@
 		'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,
 		'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,
+		'taxonomies' => array(), 'statuses' => array(), 'show_ui' => null, 'menu_position' => null, 'menu_icon' => null,
 		'permalink_epmask' => EP_PERMALINK, 'can_export' => true, 'show_in_nav_menus' => null
 	);
 	$args = wp_parse_args($args, $defaults);
@@ -958,6 +1047,10 @@
 	foreach ( $args->taxonomies as $taxonomy ) {
 		register_taxonomy_for_object_type( $taxonomy, $post_type );
 	}
+	
+	foreach ( $args->statuses as $post_status ) {
+		register_status_for_object_type( $post_status, $post_type );
+	}
 
 	return $args;
 }
@@ -1624,13 +1717,17 @@
 	$query = "SELECT post_status, COUNT( * ) AS num_posts FROM {$wpdb->posts} WHERE post_type = %s";
 	if ( 'readable' == $perm && is_user_logged_in() ) {
 		$post_type_object = get_post_type_object($type);
-		if ( !current_user_can( $post_type_object->cap->read_private_posts ) ) {
-			$cache_key .= '_' . $perm . '_' . $user->ID;
-			$query .= " AND (post_status != 'private' OR ( post_author = '$user->ID' AND post_status = 'private' ))";
+		foreach( get_post_stati( array( 'private' => true, 'object_type' => $type ) ) as $_status ) {
+			$cap_name = "read_{$_status}_posts";
+			if ( ! empty($post_type_object->cap->$cap_name) && ! current_user_can( $post_type_object->cap->$cap_name ) ) {
+				$suffix = ( 'private' == $_status ) ? '' : $_status;
+				$cache_key .= '_' . $perm . $suffix . '_' . $user->ID;
+				$query .= " AND (post_status != '$_status' OR ( post_author = '$user->ID' AND post_status = '$_status' ))";
+			}
 		}
 	}
 	$query .= ' GROUP BY post_status';
-
+	
 	$count = wp_cache_get($cache_key, 'counts');
 	if ( false !== $count )
 		return $count;
@@ -1638,7 +1735,7 @@
 	$count = $wpdb->get_results( $wpdb->prepare( $query, $type ), ARRAY_A );
 
 	$stats = array();
-	foreach ( get_post_stati() as $state )
+	foreach ( get_post_stati( array( 'object_type' => $type ) ) as $state )
 		$stats[$state] = 0;
 
 	foreach ( (array) $count as $row )
@@ -2123,7 +2220,9 @@
 		$limit = "LIMIT $num";
 	}
 
-	$sql = "SELECT * FROM $wpdb->posts WHERE post_type = 'post' AND post_status IN ( 'draft', 'publish', 'future', 'pending', 'private' ) ORDER BY post_date DESC $limit";
+	$status_names = get_post_stati( array( 'public' => true, 'object_type' => 'post' ) );
+
+	$sql = "SELECT * FROM $wpdb->posts WHERE post_type = 'post' AND post_status IN ( '" . implode( "', '", $status_names ) . "' ) ORDER BY post_date DESC $limit";
 	$result = $wpdb->get_results($sql, ARRAY_A);
 
 	return $result ? $result : array();
@@ -2271,14 +2370,17 @@
 		$post_before = get_post($post_ID);
 	}
 
-	// Don't allow contributors to set to set the post slug for pending review posts
-	if ( 'pending' == $post_status && !current_user_can( 'publish_posts' ) )
+	// Don't allow contributors to set the post slug for pending review posts
+	$post_status_obj = get_post_status_object( $post_status );
+	$draft_or_pending = in_array( $post_status, array('draft', 'auto-draft') ) || ! empty($post_status_obj->moderation);
+
+	if ( ! empty( $post_status_obj->moderation ) && ! current_user_can( 'publish_posts' ) )
 		$post_name = '';
 
 	// Create a valid post name.  Drafts and pending posts are allowed to have an empty
 	// post name.
 	if ( empty($post_name) ) {
-		if ( !in_array( $post_status, array( 'draft', 'pending', 'auto-draft' ) ) )
+		if ( ! $draft_or_pending )
 			$post_name = sanitize_title($post_title);
 		else
 			$post_name = '';
@@ -2291,7 +2393,7 @@
 		$post_date = current_time('mysql');
 
 	if ( empty($post_date_gmt) || '0000-00-00 00:00:00' == $post_date_gmt ) {
-		if ( !in_array( $post_status, array( 'draft', 'pending', 'auto-draft' ) ) )
+		if ( ! $draft_or_pending )
 			$post_date_gmt = get_gmt_from_date($post_date);
 		else
 			$post_date_gmt = '0000-00-00 00:00:00';
@@ -2305,7 +2407,9 @@
 		$post_modified_gmt = $post_date_gmt;
 	}
 
-	if ( 'publish' == $post_status ) {
+	$set_status = $post_status;
+	
+	if ( $post_status_obj->public || $post_status_obj->private ) {
 		$now = gmdate('Y-m-d H:i:59');
 		if ( mysql2date('U', $post_date_gmt, false) > mysql2date('U', $now, false) )
 			$post_status = 'future';
@@ -2315,6 +2419,11 @@
 			$post_status = 'publish';
 	}
 
+	if ( ( 'future' == $post_status ) && ( 'publish' != $set_status ) ) {
+		update_post_meta( $post_ID, '_scheduled_status', $set_status );
+	} else
+		delete_post_meta( $post_ID, '_scheduled_status' );
+
 	if ( empty($comment_status) ) {
 		if ( $update )
 			$comment_status = 'closed';
@@ -2345,7 +2454,7 @@
 	else
 		$menu_order = 0;
 
-	if ( !isset($post_password) || 'private' == $post_status )
+	if ( ! isset($post_password) || $post_status_obj->private )
 		$post_password = '';
 
 	$post_name = wp_unique_post_slug($post_name, $post_ID, $post_status, $post_type, $post_parent);
@@ -2386,7 +2495,10 @@
 		$where = array( 'ID' => $post_ID );
 	}
 
-	if ( empty($data['post_name']) && !in_array( $data['post_status'], array( 'draft', 'pending', 'auto-draft' ) ) ) {
+	$post_status_obj = get_post_status_object( $data['post_status'] );
+	$draft_or_pending = in_array( $data['post_status'], array('draft', 'auto-draft') ) || ! empty($post_status_obj->moderation);
+	
+	if ( empty($data['post_name']) && ! $draft_or_pending ) {
 		$data['post_name'] = sanitize_title($data['post_title'], $post_ID);
 		$wpdb->update( $wpdb->posts, array( 'post_name' => $data['post_name'] ), $where );
 	}
@@ -2479,10 +2591,11 @@
 		$post_cats = $post['post_category'];
 
 	// Drafts shouldn't be assigned a date unless explicitly done so by the user
-	if ( isset( $post['post_status'] ) && in_array($post['post_status'], array('draft', 'pending', 'auto-draft')) && empty($postarr['edit_date']) &&
-			 ('0000-00-00 00:00:00' == $post['post_date_gmt']) )
-		$clear_date = true;
-	else
+	if ( isset( $post['post_status'] ) && empty($postarr['edit_date']) && ('0000-00-00 00:00:00' == $post['post_date_gmt']) ) {
+		$post_status_obj = get_post_status_object( $post['post_status'] );
+
+		$clear_date = in_array( $post['post_status'], array('draft', 'auto-draft') ) || ! empty($post_status_obj->moderation);
+	} else
 		$clear_date = false;
 
 	// Merge old and new fields with new fields overwriting old ones.
@@ -2520,8 +2633,14 @@
 	if ( 'publish' == $post->post_status )
 		return;
 
-	$wpdb->update( $wpdb->posts, array( 'post_status' => 'publish' ), array( 'ID' => $post_id ) );
+	if ( 'future' == $post->post_status ) {
+		if ( ! $post_status = get_post_meta( $post_id, '_scheduled_status', true ) )
+			$post_status = 'publish';	
+	} else
+		$post_status = 'publish';
 
+	$wpdb->update( $wpdb->posts, array( 'post_status' => $post_status ), array( 'ID' => $post_id ) );
+
 	$old_status = $post->post_status;
 	$post->post_status = 'publish';
 	wp_transition_post_status('publish', $old_status, $post);
@@ -2583,7 +2702,9 @@
  * @return string unique slug for the post, based on $post_name (with a -1, -2, etc. suffix)
  */
 function wp_unique_post_slug( $slug, $post_ID, $post_status, $post_type, $post_parent ) {
-	if ( in_array( $post_status, array( 'draft', 'pending', 'auto-draft' ) ) )
+	$post_status_obj = get_post_status_object( $post_status );
+
+	if ( in_array( $post['post_status'], array('draft', 'auto-draft') ) || ! empty($post_status_obj->moderation) )
 		return $slug;
 
 	global $wpdb, $wp_rewrite;
@@ -3127,7 +3248,7 @@
 		return false;
 
 	// Make sure we have a valid post status
-	if ( !in_array($post_status, get_post_stati()) )
+	if ( ! in_array( $post_status, get_post_stati( array( 'object_type' => $post_type ) ) ) )
 		return false;
 
 	$cache = array();
Index: wp-includes/query.php
===================================================================
--- wp-includes/query.php	(revision 15818)
+++ wp-includes/query.php	(working copy)
@@ -2033,17 +2033,18 @@
 			$post_type_object = get_post_type_object ( 'post' );
 		}
 
+		$_post_type = ( $post_type ) ? $post_type : 'post';  // corresponds to hardcoded default for POST_TYPE clause
+		$type_arg = ( 'any' == $_post_type ) ? array() : array( 'object_type' => $_post_type );
+
 		if ( !empty($post_type_object) ) {
 			$post_type_cap = $post_type_object->capability_type;
 			$edit_cap = $post_type_object->cap->edit_post;
 			$read_cap = $post_type_object->cap->read_post;
 			$edit_others_cap = $post_type_object->cap->edit_others_posts;
-			$read_private_cap = $post_type_object->cap->read_private_posts;
 		} else {
 			$edit_cap = 'edit_' . $post_type_cap;
 			$read_cap = 'read_' . $post_type_cap;
 			$edit_others_cap = 'edit_others_' . $post_type_cap . 's';
-			$read_private_cap = 'read_private_' . $post_type_cap . 's';
 		}
 
 		if ( isset($q['post_status']) && '' != $q['post_status'] ) {
@@ -2051,24 +2052,36 @@
 			$q_status = explode(',', $q['post_status']);
 			$r_status = array();
 			$p_status = array();
+			$p_status_owner = array();
 			$e_status = array();
 			if ( $q['post_status'] == 'any' ) {
 				foreach ( get_post_stati( array('exclude_from_search' => true) ) as $status )
 					$e_status[] = "$wpdb->posts.post_status <> '$status'";
 			} else {
-				foreach ( get_post_stati() as $status ) {
+				$_args = array_merge( array(), $type_arg );
+				foreach ( get_post_stati( $_args, 'object' ) as $status => $status_obj ) {
 					if ( in_array( $status, $q_status ) ) {
-						if ( 'private' == $status )
-							$p_status[] = "$wpdb->posts.post_status = '$status'";
-						else
+						if ( $status_obj->private ) {
+							if ( ! empty($post_type_object) ) {
+								$check_cap = "read_{$status}_posts";
+								$read_private_cap = ( ! empty( $post_type_object->cap->$check_cap ) ) ? $post_type_object->cap->$check_cap : $post_type_object->cap->read_private_posts;
+							} else
+								$read_private_cap = 'read_private_' . $post_type_cap . 's';
+							
+							if ( ! empty($q['perm'] ) && 'readable' == $q['perm'] && ! current_user_can( $read_private_cap ) )
+								$p_status_owner[] = "$wpdb->posts.post_status = '$status'";
+							else
+								$p_status[] = "$wpdb->posts.post_status = '$status'";
+						} else
 							$r_status[] = "$wpdb->posts.post_status = '$status'";
 					}
 				}
 			}
 
 			if ( empty($q['perm'] ) || 'readable' != $q['perm'] ) {
-				$r_status = array_merge($r_status, $p_status);
+				$r_status = array_merge($r_status, $p_status, $p_status_owner);
 				unset($p_status);
+				unset($p_status_owner);
 			}
 
 			if ( !empty($e_status) ) {
@@ -2080,24 +2093,26 @@
 				else
 					$statuswheres[] = "(" . join( ' OR ', $r_status ) . ")";
 			}
-			if ( !empty($p_status) ) {
-				if ( !empty($q['perm'] ) && 'readable' == $q['perm'] && !current_user_can($read_private_cap) )
-					$statuswheres[] = "($wpdb->posts.post_author = $user_ID " .  "AND (" . join( ' OR ', $p_status ) . "))";
-				else
-					$statuswheres[] = "(" . join( ' OR ', $p_status ) . ")";
+			if ( ! empty( $p_status_owner ) ) {
+				$statuswheres[] = "($wpdb->posts.post_author = $user_ID " .  "AND (" . join( ' OR ', $p_status_owner ) . "))";
 			}
+			if ( ! empty( $p_status ) ) {			
+				$statuswheres[] = "(" . join( ' OR ', $p_status ) . ")";
+			}
 			if ( $post_status_join ) {
 				$join .= " LEFT JOIN $wpdb->posts AS p2 ON ($wpdb->posts.post_parent = p2.ID) ";
 				foreach ( $statuswheres as $index => $statuswhere )
 					$statuswheres[$index] = "($statuswhere OR ($wpdb->posts.post_status = 'inherit' AND " . str_replace($wpdb->posts, 'p2', $statuswhere) . "))";
 			}
 			foreach ( $statuswheres as $statuswhere )
-				$where .= " AND $statuswhere";
+				$where .= " AND $statuswhere";	
+
 		} elseif ( !$this->is_singular ) {
 			$where .= " AND ($wpdb->posts.post_status = 'publish'";
 
 			// Add public states.
-			$public_states = get_post_stati( array('public' => true) );
+			$_args = array_merge( array( 'public' => true ), $type_arg );
+			$public_states = apply_filters_ref_array( 'posts_public_stati', array( get_post_stati( $_args ), &$this ) );
 			foreach ( (array) $public_states as $state ) {
 				if ( 'publish' == $state ) // Publish is hard-coded above.
 					continue;
@@ -2106,16 +2121,25 @@
 
 			if ( is_admin() ) {
 				// Add protected states that should show in the admin all list.
-				$admin_all_states = get_post_stati( array('protected' => true, 'show_in_admin_all_list' => true) );
+				$_args = array_merge( array( 'protected' => true, 'show_in_admin_all_list' => true ), $type_arg );
+				$admin_all_states = get_post_stati( $_args );
 				foreach ( (array) $admin_all_states as $state )
 					$where .= " OR $wpdb->posts.post_status = '$state'";
 			}
 
 			if ( is_user_logged_in() ) {
 				// Add private states that are limited to viewing by the author of a post or someone who has caps to read private states.
-				$private_states = get_post_stati( array('private' => true) );
-				foreach ( (array) $private_states as $state )
+				$_args = array_merge( array( 'private' => true ), $type_arg );
+				$private_states = apply_filters_ref_array( 'posts_private_stati', array( get_post_stati( $_args ), &$this ) );
+				foreach ( (array) $private_states as $state ) {
+					if ( ! empty($post_type_object) ) {
+						$check_cap = "read_{$status}_posts";
+						$read_private_cap = ( ! empty( $post_type_object->cap->$check_cap ) ) ? $post_type_object->cap->$check_cap : $post_type_object->cap->read_private_posts;
+					} else
+						$read_private_cap = 'read_private_' . $post_type_cap . 's';
+
 					$where .= current_user_can( $read_private_cap ) ? " OR $wpdb->posts.post_status = '$state'" : " OR $wpdb->posts.post_author = $user_ID AND $wpdb->posts.post_status = '$state'";
+				}
 			}
 
 			$where .= ')';
@@ -2158,8 +2182,11 @@
 				$cwhere = "WHERE comment_approved = '1' $where";
 				$cgroupby = "$wpdb->comments.comment_id";
 			} else { // Other non singular e.g. front
+				$public_stati = apply_filters( 'comment_feed_stati', get_post_stati( array( 'public' => true ) ) );
+				$public_csv = implode( "', '", $public_stati );
+
 				$cjoin = "JOIN $wpdb->posts ON ( $wpdb->comments.comment_post_ID = $wpdb->posts.ID )";
-				$cwhere = "WHERE post_status = 'publish' AND comment_approved = '1'";
+				$cwhere = "WHERE post_status IN ( '$public_csv' ) AND comment_approved = '1'";
 				$cgroupby = '';
 			}
 
@@ -2337,11 +2364,13 @@
 					$stickies_where = "AND $wpdb->posts.post_type IN ('" . $post_types . "')";
 				}
 
+				$public_stati = apply_filters_ref_array( 'posts_sticky_stati', array( get_post_stati( array( 'public' => true ) ), &$this ) );
 				$stickies = $wpdb->get_results( "SELECT * FROM $wpdb->posts WHERE $wpdb->posts.ID IN ($stickies__in) $stickies_where" );
 				foreach ( $stickies as $sticky_post ) {
 					// Ignore sticky posts the current user cannot read or are not published.
-					if ( 'publish' != $sticky_post->post_status )
+					if ( ! in_array( $sticky_post->post_status, $public_stati ) )
 						continue;
+
 					array_splice($this->posts, $sticky_offset, 0, array($sticky_post));
 					$sticky_offset++;
 				}
Index: wp-includes/script-loader.php
===================================================================
--- wp-includes/script-loader.php	(revision 15818)
+++ wp-includes/script-loader.php	(working copy)
@@ -290,7 +290,7 @@
 
 		$scripts->add( 'post', "/wp-admin/js/post$suffix.js", array('suggest', 'wp-lists', 'postbox'), '20100526' );
 		$scripts->add_data( 'post', 'group', 1 );
-		$scripts->localize( 'post', 'postL10n', array(
+		$arr = array(
 			'tagsUsed' =>  __('Tags used on this post:'),
 			'add' => esc_attr(__('Add')),
 			'addTag' => esc_attr(__('Add new tag')),
@@ -305,6 +305,7 @@
 			'endcomm' => __('No more comments found.'),
 			'publish' => __('Publish'),
 			'schedule' => __('Schedule'),
+			'update' => __('Update'),
 			'updatePost' => __('Update Post'),
 			'updatePage' => __('Update Page'),
 			'savePending' => __('Save as Pending'),
@@ -316,8 +317,23 @@
 			'privatelyPublished' => __('Privately Published'),
 			'published' => __('Published'),
 			'l10n_print_after' => 'try{convertEntities(postL10n);}catch(e){};'
-		) );
+		);
+		
+		$custom = array();
+		foreach( get_post_stati( array( 'public' => true, 'private' => true ), 'object', 'or' ) as $_status => $_status_obj ) {
+			if ( 'publish' == $_status )
+				continue;
 
+			$custom[ $_status ] = $_status_obj->labels->visibility;
+
+			if ( $_status_obj->public )
+				$custom[ $_status . 'Sticky' ] = sprintf( __('%s, Sticky'), $_status_obj->label );
+		}
+
+		$arr = array_merge( $custom, $arr );  // note: 'publish' has existing value of 'Publish', cannot be used to reference status name 
+
+		$scripts->localize( 'post', 'postL10n', $arr );
+
 		$scripts->add( 'link', "/wp-admin/js/link$suffix.js", array('wp-lists', 'postbox'), '20090526' );
 		$scripts->add_data( 'link', 'group', 1 );
 
Index: wp-includes/taxonomy.php
===================================================================
--- wp-includes/taxonomy.php	(revision 15818)
+++ wp-includes/taxonomy.php	(working copy)
@@ -109,6 +109,9 @@
 
 	$field = ('names' == $output) ? 'name' : false;
 
+	if ( isset( $args['object_type'] ) )
+		$args['object_type'] = (array) $args['object_type'];
+
 	return wp_filter_object_list($wp_taxonomies, $args, $operator, $field);
 }
 
@@ -2516,7 +2519,11 @@
 	// Get the object and term ids and stick them in a lookup table
 	$tax_obj = get_taxonomy($taxonomy);
 	$object_types = esc_sql($tax_obj->object_type);
-	$results = $wpdb->get_results("SELECT object_id, term_taxonomy_id FROM $wpdb->term_relationships INNER JOIN $wpdb->posts ON object_id = ID WHERE term_taxonomy_id IN (" . implode(',', array_keys($term_ids)) . ") AND post_type IN ('" . implode("', '", $object_types) . "') AND post_status = 'publish'");
+	
+	$public_stati = apply_filters( 'term_count_stati', get_post_stati( array( 'public' => true, 'object_type' => $tax_obj->object_type ) ) );
+	$public_csv = implode( "', '", $public_stati );
+
+	$results = $wpdb->get_results("SELECT object_id, term_taxonomy_id FROM $wpdb->term_relationships INNER JOIN $wpdb->posts ON object_id = ID WHERE term_taxonomy_id IN (" . implode(',', array_keys($term_ids)) . ") AND post_type IN ('" . implode("', '", $object_types) . "') AND post_status IN ('$public_csv')");
 	foreach ( $results as $row ) {
 		$id = $term_ids[$row->term_taxonomy_id];
 		$term_items[$id][$row->object_id] = isset($term_items[$id][$row->object_id]) ? ++$term_items[$id][$row->object_id] : 1;
@@ -2565,8 +2572,11 @@
 	$object_types = is_array($taxonomy->object_type) ? $taxonomy->object_type : array($taxonomy->object_type);
 	$object_types = esc_sql($object_types);
 
+	$public_stati = apply_filters( 'term_count_stati', get_post_stati( array( 'public' => true, 'object_type' => $taxonomy->object_type ) ) );
+	$public_csv = implode( "', '", $public_stati );
+
 	foreach ( (array) $terms as $term ) {
-		$count = $wpdb->get_var( $wpdb->prepare( "SELECT COUNT(*) FROM $wpdb->term_relationships, $wpdb->posts WHERE $wpdb->posts.ID = $wpdb->term_relationships.object_id AND post_status = 'publish' AND post_type IN ('" . implode("', '", $object_types) . "') AND term_taxonomy_id = %d", $term ) );
+		$count = $wpdb->get_var( $wpdb->prepare( "SELECT COUNT(*) FROM $wpdb->term_relationships, $wpdb->posts WHERE $wpdb->posts.ID = $wpdb->term_relationships.object_id AND post_status IN ('$public_csv') AND post_type IN ('" . implode("', '", $object_types) . "') AND term_taxonomy_id = %d", $term ) );
 		do_action( 'edit_term_taxonomy', $term, $taxonomy );
 		$wpdb->update( $wpdb->term_taxonomy, compact( 'count' ), array( 'term_taxonomy_id' => $term ) );
 		do_action( 'edited_term_taxonomy', $term, $taxonomy );
