Index: wp-includes/media.php
===================================================================
--- wp-includes/media.php	(revision 21940)
+++ wp-includes/media.php	(working copy)
@@ -986,6 +986,53 @@
 }
 
 /**
+ * Return all of the taxonomy names that are registered for attachments.
+ *
+ * @since 3.5.0
+ *
+ * @uses $wp_taxonomies
+ *
+ * @param string $output The type of output to return, either taxonomy 'names' or 'objects'. 'names' is the default.
+ * @return array The names of all taxonomy of $object_type.
+ */
+function get_taxonomies_for_attachments( $output = 'names' ) {
+	global $wp_taxonomies;
+
+	$taxonomies = array();
+	foreach ( (array) $wp_taxonomies as $tax_name => $tax_obj ) {
+		if ( in_array('attachment', (array) $tax_obj->object_type) || _is_attachment_type_taxonomy( $tax_obj->object_type ) ) {
+			if ( 'names' == $output )
+				$taxonomies[] = $tax_name;
+			else
+				$taxonomies[ $tax_name ] = $tax_obj;
+		}
+	}
+
+	return $taxonomies;
+}
+
+/**
+ * Return all of the taxonomy names that are registered for specific attachment types.
+ * Examples: attachment:image, attachment:video
+ *
+ * @since 3.5.0
+ *
+ * @param array|string $object_types The object types of the taxonomy.
+ * @return bool True if it's an attachment tax, false if not.
+ */
+function _is_attachment_type_taxonomy( $object_types ) {
+	if ( empty( $object_types ) )
+		return false;
+
+	$object_types = (array) $object_types;
+
+	foreach ( $object_types as $type ) {
+		if ( 0 === strpos( $type, 'attachment:' ) )
+			return true;
+	}
+}
+
+/**
  * Check if the installed version of GD supports particular image type
  *
  * @since 2.9.0
Index: wp-includes/link-template.php
===================================================================
--- wp-includes/link-template.php	(revision 21940)
+++ wp-includes/link-template.php	(working copy)
@@ -106,7 +106,7 @@
 	if ( $post->post_type == 'page' )
 		return get_page_link($post->ID, $leavename, $sample);
 	elseif ( $post->post_type == 'attachment' )
-		return get_attachment_link($post->ID);
+		return get_attachment_link( $post->ID, $leavename );
 	elseif ( in_array($post->post_type, get_post_types( array('_builtin' => false) ) ) )
 		return get_post_permalink($post->ID, $leavename, $sample);
 
@@ -292,9 +292,10 @@
  * @since 2.0.0
  *
  * @param mixed $post Optional. Post ID or object.
+ * @param bool $leavename Optional. Leave name.
  * @return string
  */
-function get_attachment_link( $post = null ) {
+function get_attachment_link( $post = null, $leavename = false ) {
 	global $wp_rewrite;
 
 	$link = false;
@@ -314,7 +315,10 @@
 			$name = $post->post_name;
 
 		if ( strpos($parentlink, '?') === false )
-			$link = user_trailingslashit( trailingslashit($parentlink) . $name );
+			$link = user_trailingslashit( trailingslashit($parentlink) . '%postname%' );
+
+		if ( ! $leavename )
+			$link = str_replace( '%postname%', $name, $link );
 	}
 
 	if ( ! $link )
Index: wp-includes/post.php
===================================================================
--- wp-includes/post.php	(revision 21940)
+++ wp-includes/post.php	(working copy)
@@ -53,12 +53,23 @@
 	register_post_type( 'attachment', array(
 		'labels' => array(
 			'name' => __( 'Media' ),
-			'edit_item' => __( 'Edit Media' ),
+			'name' => _x('Media', 'post type general name'),
+ 			'singular_name' => _x( 'Media Item', 'post type singular name' ),
+ 			'add_new' => _x( 'Add New', 'media item'),
+ 			'add_new_item' => __( 'Add New Media' ),
+ 			'edit_item' => __( 'Edit Media' ),
+			'new_item' => __( 'New Media Item' ),
+ 			'view_item' => __( 'View Attachment Page' ),
+ 			'search_items' => __( 'Search Media' ),
+ 			'not_found' => __( 'No media found.' ),
+ 			'not_found_in_trash' => __('No media found in Trash.'),
+ 			'parent_item_colon' => __('Parent:'),
+ 			'all_items' => __( 'All Media' ),
 		),
 		'public' => true,
-		'show_ui' => false,
+		'show_ui' => true,
 		'_builtin' => true, /* internal use only. don't use this when registering your own post type. */
-		'_edit_link' => 'media.php?attachment_id=%d', /* 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,
@@ -66,7 +77,7 @@
 		'query_var' => false,
 		'show_in_nav_menus' => false,
 		'delete_with_user' => true,
-		'supports' => array( 'comments', 'author' ),
+		'supports' => array( 'title', 'editor', 'author', 'comments', 'image-editor' ),
 	) );
 
 	register_post_type( 'revision', array(
@@ -3782,13 +3793,16 @@
 	if ( ! in_array( $post_status, array( 'inherit', 'private' ) ) )
 		$post_status = 'inherit';
 
-	// Make sure we set a valid category.
-	if ( !isset($post_category) || 0 == count($post_category) || !is_array($post_category) ) {
-		// 'post' requires at least one category.
-		if ( 'post' == $post_type )
-			$post_category = array( get_option('default_category') );
-		else
-			$post_category = array();
+
+	// Support for all taxonomies
+	if ( ! empty( $tax_input ) ) {
+		foreach ( $tax_input as $taxonomy => $tags ) {
+			$taxonomy_obj = get_taxonomy( $taxonomy );
+			if ( is_array( $tags ) ) // array = hierarchical, string = non-hierarchical.
+				$tags = array_filter( $tags );
+			if ( current_user_can( $taxonomy_obj->cap->assign_terms ) )
+				wp_set_post_terms( $post_ID, $tags, $taxonomy );
+		}
 	}
 
 	// Are we updating or creating?
@@ -3873,8 +3887,6 @@
 		$wpdb->update( $wpdb->posts, compact("post_name"), array( 'ID' => $post_ID ) );
 	}
 
-	wp_set_post_categories($post_ID, $post_category);
-
 	if ( $file )
 		update_attached_file( $post_ID, $file );
 
Index: wp-includes/template.php
===================================================================
--- wp-includes/template.php	(revision 21940)
+++ wp-includes/template.php	(working copy)
@@ -304,6 +304,7 @@
 function get_attachment_template() {
 	global $posts;
 	$type = explode('/', $posts[0]->post_mime_type);
+	var_dump($posts[0]->post_mime_type);
 	if ( $template = get_query_template($type[0]) )
 		return $template;
 	elseif ( $template = get_query_template($type[1]) )
@@ -391,4 +392,3 @@
 	else
 		require( $_template_file );
 }
-
Index: wp-admin/includes/class-wp-terms-list-table.php
===================================================================
--- wp-admin/includes/class-wp-terms-list-table.php	(revision 21940)
+++ wp-admin/includes/class-wp-terms-list-table.php	(working copy)
@@ -299,7 +299,9 @@
 		if ( 'post' != $this->screen->post_type )
 			$args['post_type'] = $this->screen->post_type;
 
-		return "<a href='" . esc_url ( add_query_arg( $args, 'edit.php' ) ) . "'>$count</a>";
+		$base = $this->screen->post_type == 'attachment' ? 'upload.php' : 'edit.php';
+
+		return "<a href='" . esc_url ( add_query_arg( $args, $base ) ) . "'>$count</a>";
 	}
 
 	function column_links( $tag ) {
Index: wp-admin/includes/post.php
===================================================================
--- wp-admin/includes/post.php	(revision 21940)
+++ wp-admin/includes/post.php	(working copy)
@@ -235,6 +235,16 @@
 		}
 	}
 
+	// Attachment stuff
+	if ( isset( $post_data['_wp_attachment_image_alt'] ) ) {
+		$image_alt = get_post_meta( $post_ID, '_wp_attachment_image_alt', true );
+		if ( $image_alt != stripslashes( $post_data['_wp_attachment_image_alt'] ) ) {
+			$image_alt = wp_strip_all_tags( stripslashes( $post_data['_wp_attachment_image_alt'] ), true );
+			// update_meta expects slashed
+			update_post_meta( $post_ID, '_wp_attachment_image_alt', addslashes( $image_alt ) );
+		}
+	}
+
 	add_meta( $post_ID );
 
 	update_post_meta( $post_ID, '_edit_last', $GLOBALS['current_user']->ID );
@@ -1064,7 +1074,7 @@
 
 	list($permalink, $post_name) = get_sample_permalink($post->ID, $new_title, $new_slug);
 
-	if ( 'publish' == $post->post_status ) {
+	if ( 'publish' == get_post_status( $post ) ) {
 		$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/screen.php
===================================================================
--- wp-admin/includes/screen.php	(revision 21940)
+++ wp-admin/includes/screen.php	(working copy)
@@ -98,6 +98,8 @@
 		if ( 'post' == $screen->base ) {
 			if ( 'post' == $screen->post_type || 'page' == $screen->post_type )
 				$hidden = array('slugdiv', 'trackbacksdiv', 'postcustom', 'postexcerpt', 'commentstatusdiv', 'commentsdiv', 'authordiv', 'revisionsdiv');
+			elseif ( 'attachment' == $screen->post_type )
+				$hidden = array( 'slugdiv', 'trackbacksdiv', 'postcustom', 'authordiv', 'revisionsdiv' );
 			else
 				$hidden = array( 'slugdiv' );
 		}
Index: wp-admin/includes/media.php
===================================================================
--- wp-admin/includes/media.php	(revision 21940)
+++ wp-admin/includes/media.php	(working copy)
@@ -857,9 +857,9 @@
 
 /**
  * Filters input from media_upload_form_handler() and assigns a default
- * post_title from the file name if none supplied. 
+ * post_title from the file name if none supplied.
  *
- * Illustrates the use of the attachment_fields_to_save filter 
+ * Illustrates the use of the attachment_fields_to_save filter
  * which can be used to add default values to any field before saving to DB.
  *
  * @since 2.5.0
@@ -2095,6 +2095,63 @@
 	echo '<p>' . sprintf( __( 'Sorry, you have used all of your storage quota of %s MB.' ), get_space_allowed() ) . '</p>';
 }
 
+/**
+ * Displays the image and editor in the post editor
+ *
+ * @since 3.5.0
+ */
+function edit_form_image_editor() {
+	$post = get_post();
+
+	if ( ( $attachment_id = intval( $post->ID ) ) && $thumb_url = wp_get_attachment_image_src( $attachment_id, 'thumbnail', true ) )
+		$thumb_url = $thumb_url[0];
+	else
+		$thumb_url = false;
+
+	$filename = esc_html( basename( $post->guid ) );
+	$title = esc_attr( $post->post_title );
+
+
+	$post_mime_types = get_post_mime_types();
+	$keys = array_keys( wp_match_mime_types( array_keys( $post_mime_types ), $post->post_mime_type ) );
+	$type = array_shift( $keys );
+	$type_html = "<input type='hidden' id='type-of-$attachment_id' value='" . esc_attr( $type ) . "' />";
+
+
+	$media_dims = '';
+	$meta = wp_get_attachment_metadata( $post->ID );
+	if ( is_array( $meta ) && array_key_exists( 'width', $meta ) && array_key_exists( 'height', $meta ) )
+		$media_dims .= "<span id='media-dims-$post->ID'>{$meta['width']}&nbsp;&times;&nbsp;{$meta['height']}</span> ";
+	$media_dims = apply_filters( 'media_meta', $media_dims, $post );
+
+	$image_edit_button = '';
+	if ( gd_edit_image_support( $post->post_mime_type ) ) {
+		$nonce = wp_create_nonce( "image_editor-$post->ID" );
+		$image_edit_button = "<input type='button' id='imgedit-open-btn-$post->ID' onclick='imageEdit.open( $post->ID, \"$nonce\" )' class='button' value='" . esc_attr__( 'Edit Image' ) . "' /> <img src='" . esc_url( admin_url( 'images/wpspin_light.gif' ) ) . "' class='imgedit-wait-spin' alt='' />";
+	}
+
+ 	?>
+	<div class="wp_attachment_holder">
+		<div class="imgedit-response" id="imgedit-response-<?php echo $attachment_id; ?>"></div>
+
+		<div class="wp_attachment_image" id="media-head-<?php echo $attachment_id; ?>">
+			<p><img class="thumbnail" src="<?php echo $thumb_url; ?>" alt="" /></p>
+			<p><?php echo $image_edit_button; ?></p>
+		</div>
+		<div style="display:none" class="image-editor" id="image-editor-<?php echo $attachment_id; ?>"></div>
+
+		<div class="wp_attachment_details">
+			<p><strong><?php _e( 'File name:' ); ?></strong> <?php echo $filename; ?></p>
+			<p><strong><?php _e( 'File type:' ); ?></strong> <?php echo $post->post_mime_type; ?></p>
+			<?php
+				if ( !empty( $media_dims ) )
+					echo '<p><strong>' . __( 'Dimensions:' ) . "</strong> $media_dims</p>";
+			?>
+		</div>
+	</div>
+	<?php
+}
+
 add_filter( 'async_upload_image', 'get_media_item', 10, 2 );
 add_filter( 'async_upload_audio', 'get_media_item', 10, 2 );
 add_filter( 'async_upload_video', 'get_media_item', 10, 2 );
Index: wp-admin/includes/class-wp-media-list-table.php
===================================================================
--- wp-admin/includes/class-wp-media-list-table.php	(revision 21940)
+++ wp-admin/includes/class-wp-media-list-table.php	(working copy)
@@ -132,7 +132,24 @@
 		/* translators: column name */
 		$posts_columns['title'] = _x( 'File', 'column name' );
 		$posts_columns['author'] = __( 'Author' );
-		//$posts_columns['tags'] = _x( 'Tags', 'column name' );
+
+		$taxonomies = array();
+
+		$taxonomies = get_taxonomies_for_attachments( 'objects' );
+		$taxonomies = wp_filter_object_list( $taxonomies, array( 'show_admin_column' => true ), 'and', 'name' );
+
+		$taxonomies = apply_filters( "manage_taxonomies_for_attachment_columns", $taxonomies, 'attachment' );
+		$taxonomies = array_filter( $taxonomies, 'taxonomy_exists' );
+
+		foreach ( $taxonomies as $taxonomy ) {
+			if ( 'post_tag' == $taxonomy )
+				$column_key = 'tags';
+			else
+				$column_key = 'taxonomy-' . $taxonomy;
+
+			$posts_columns[ $column_key ] = get_taxonomy( $taxonomy )->labels->name;
+		}
+
 		/* translators: column name */
 		if ( !$this->detached ) {
 			$posts_columns['parent'] = _x( 'Attached to', 'column name' );
@@ -251,23 +268,6 @@
 <?php
 		break;
 
-	case 'tags':
-?>
-		<td <?php echo $attributes ?>><?php
-		$tags = get_the_tags();
-		if ( !empty( $tags ) ) {
-			$out = array();
-			foreach ( $tags as $c )
-				$out[] = "<a href='edit.php?tag=$c->slug'> " . esc_html( sanitize_term_field( 'name', $c->name, $c->term_id, 'post_tag', 'display' ) ) . "</a>";
-			echo join( ', ', $out );
-		} else {
-			_e( 'No Tags' );
-		}
-?>
-		</td>
-<?php
-		break;
-
 	case 'desc':
 ?>
 		<td <?php echo $attributes ?>><?php echo has_excerpt() ? $post->post_excerpt : ''; ?></td>
@@ -339,6 +339,34 @@
 		break;
 
 	default:
+		if ( 'tags' == $column_name )
+			$taxonomy = 'post_tag';
+		elseif ( 0 === strpos( $column_name, 'taxonomy-' ) )
+			$taxonomy = substr( $column_name, 9 );
+
+		if ( ! empty( $taxonomy ) ) {
+			$taxonomy_object = get_taxonomy( $taxonomy );
+			echo '<td ' . $attributes . '>';
+			if ( $terms = get_the_terms( $post->ID, $taxonomy ) ) {
+				$out = array();
+				foreach ( $terms as $t ) {
+					$posts_in_term_qv = array();
+					$posts_in_term_qv['taxonomy'] = $taxonomy;
+					$posts_in_term_qv['term'] = $t->slug;
+
+					$out[] = sprintf( '<a href="%s">%s</a>',
+						esc_url( add_query_arg( $posts_in_term_qv, 'upload.php' ) ),
+						esc_html( sanitize_term_field( 'name', $t->name, $t->term_id, $taxonomy, 'display' ) )
+					);
+				}
+				/* translators: used between list items, there is a space after the comma */
+				echo join( __( ', ' ), $out );
+			} else {
+				echo '&#8212;';
+			}
+			echo '</td>';
+			break;
+		}
 ?>
 		<td <?php echo $attributes ?>>
 			<?php do_action( 'manage_media_custom_column', $column_name, $id ); ?>
Index: wp-admin/includes/meta-boxes.php
===================================================================
--- wp-admin/includes/meta-boxes.php	(revision 21940)
+++ wp-admin/includes/meta-boxes.php	(working copy)
@@ -51,7 +51,7 @@
 </div>
 <?php endif; // public post type ?>
 <div class="clear"></div>
-</div><?php // /minor-publishing-actions ?>
+</div><!-- /minor-publishing-actions -->
 
 <div id="misc-publishing-actions">
 
@@ -103,7 +103,7 @@
 </div>
 
 <?php } ?>
-</div><?php // /misc-pub-section ?>
+</div><!-- /misc-pub-section -->
 
 <div class="misc-pub-section" id="visibility">
 <?php _e('Visibility:'); ?> <span id="post-visibility-display"><?php
@@ -148,15 +148,15 @@
 </div>
 <?php } ?>
 
-</div><?php // /misc-pub-section ?>
+</div><!-- /misc-pub-section -->
 
 <?php
 // translators: Publish box date format, see http://php.net/date
 $datef = __( 'M j, Y @ G:i' );
 if ( 0 != $post->ID ) {
-	if ( 'future' == $post->post_status ) { // scheduled for publishing at a future date
+	if ( 'future' == get_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 ( 'publish' == get_post_status() || 'private' == get_post_status() ) { // 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>');
@@ -230,6 +230,77 @@
 }
 
 /**
+ * Display attachment submit form fields.
+ *
+ * @since 3.5.0
+ *
+ * @param object $post
+ */
+function attachment_submit_meta_box( $post ) {
+	global $action;
+
+	$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);
+?>
+<div class="submitbox" id="submitpost">
+
+<div id="minor-publishing">
+
+<?php // Hidden submit button early on so that the browser chooses the right button when form is submitted with Return key ?>
+<div style="display:none;">
+<?php submit_button( __( 'Save' ), 'button', 'save' ); ?>
+</div>
+
+
+<div id="misc-publishing-actions">
+	<?php
+	// translators: Publish box date format, see http://php.net/date
+	$datef = __( 'M j, Y @ G:i' );
+	$stamp = __('Uploaded on: <b>%1$s</b>');
+	$date = date_i18n( $datef, strtotime( $post->post_date ) );
+
+	if ( $can_publish ) : // Contributors don't get to choose the date of publish ?>
+	<div class="misc-pub-section curtime">
+		<span id="timestamp">
+		<?php printf($stamp, $date); ?></span>
+		<a href="#edit_timestamp" class="edit-timestamp hide-if-no-js"><?php _e('Edit') ?></a>
+		<div id="timestampdiv" class="hide-if-js"><?php touch_time(($action == 'edit'), 1); ?></div>
+	</div><?php // /misc-pub-section ?>
+	<?php endif; ?>
+
+	<?php do_action('attachment_submitbox_misc_actions'); ?>
+</div><!-- / misc-publishing-actions -->
+<div class="clear"></div>
+</div><!-- / minor-publishing -->
+
+<div id="major-publishing-actions">
+	<div id="delete-action">
+	<?php
+	if ( current_user_can( 'delete_post', $post->ID ) )
+		if ( EMPTY_TRASH_DAYS && MEDIA_TRASH ) {
+			echo "<a class='submitdelete deletion' href='" . get_delete_post_link( $post->ID ) . "'>" . __( 'Trash' ) . "</a>";
+		} else {
+			$delete_ays = ! MEDIA_TRASH ? " onclick='return showNotice.warn();'" : '';
+			echo  "<a class='submitdelete deletion'$delete_ays href='" . get_delete_post_link( $post->ID, null, true ) . "''>" . __( 'Delete Permanently' ) . "</a>";
+		}
+	?>
+	</div>
+
+	<div id="publishing-action">
+		<img src="<?php echo esc_url( admin_url( 'images/wpspin_light.gif' ) ); ?>" class="ajax-loading" id="ajax-loading" alt="" />
+		<input name="original_publish" type="hidden" id="original_publish" value="<?php esc_attr_e('Update') ?>" />
+		<?php submit_button( __( 'Update' ), 'primary', 'publish', false, array( 'accesskey' => 'p' ) ); ?>
+	</div>
+	<div class="clear"></div>
+</div><!-- /major-publishing-actions -->
+
+</div>
+
+<?php
+}
+
+/**
  * Display post format form elements.
  *
  * @since 3.1.0
@@ -982,4 +1053,32 @@
 		<a href="#" class="remove"><?php _e( 'Remove Featured Image' ); ?></a>
 	</div>
 	<?php
-}
\ No newline at end of file
+}
+
+/**
+ * Display attachment/media-specific information
+ *
+ * @since 3.5.0
+ *
+ * @param object $post
+ */
+function attachment_data_meta_box( $post ) {
+	$att_url = wp_get_attachment_url( $post->ID );
+	$alt_text = get_post_meta( $post->ID, '_wp_attachment_image_alt', true );
+?>
+	<label for="_wp_attachment_image_alt"><strong>Alternative text</strong></label><br />
+	<input type="text" class="widefat" name="_wp_attachment_image_alt" value="<?php echo esc_attr( $alt_text ); ?>" />
+</p>
+
+<p>
+	<label for="excerpt"><strong>Excerpt</strong></label><br />
+	<textarea class="widefat" name="excerpt"><?php echo $post->post_excerpt; ?></textarea>
+</p>
+
+<p>
+	<label for="attachment_url"><strong>File URL</strong></label><br />
+	<input type="text" class="widefat urlfield" readonly="readonly" name="attachment_url" value="<?php echo esc_attr($att_url); ?>" /><br />
+	<em><?php _e( 'Location of the uploaded file.' ); ?></em>
+</p>
+<?php
+}
Index: wp-admin/edit-tags.php
===================================================================
--- wp-admin/edit-tags.php	(revision 21940)
+++ wp-admin/edit-tags.php	(working copy)
@@ -26,7 +26,7 @@
 $title = $tax->labels->name;
 
 if ( 'post' != $post_type ) {
-	$parent_file = "edit.php?post_type=$post_type";
+	$parent_file = ( 'attachment' == $post_type ) ? 'upload.php' : "edit.php?post_type=$post_type";
 	$submenu_file = "edit-tags.php?taxonomy=$taxonomy&amp;post_type=$post_type";
 } else if ( 'link_category' == $tax->name ) {
 	$parent_file = 'link-manager.php';
Index: wp-admin/css/wp-admin.css
===================================================================
--- wp-admin/css/wp-admin.css	(revision 21940)
+++ wp-admin/css/wp-admin.css	(working copy)
@@ -3750,7 +3750,7 @@
 }
 
 .upload-php .fixed .column-parent {
-	width: 25%;
+	width: 15%;
 }
 
 .js .html-uploader #plupload-upload-ui {
@@ -3929,7 +3929,8 @@
 	margin: 8px 0;
 }
 
-.describe .imgedit-wrap table td {
+.describe .imgedit-wrap table td,
+.wp_attachment_holder .imgedit-wrap table td {
 	vertical-align: top;
 	padding-top: 0;
 }
Index: wp-admin/post.php
===================================================================
--- wp-admin/post.php	(revision 21940)
+++ wp-admin/post.php	(working copy)
@@ -85,8 +85,12 @@
 if ( ! $sendback ||
      strpos( $sendback, 'post.php' ) !== false ||
      strpos( $sendback, 'post-new.php' ) !== false ) {
-	$sendback = admin_url( 'edit.php' );
-	$sendback .= ( ! empty( $post_type ) ) ? '?post_type=' . $post_type : '';
+	if ( 'attachment' == $post_type ) {
+		$sendback = admin_url( 'upload.php' );
+	} else {
+		$sendback = admin_url( 'edit.php' );
+		$sendback .= ( ! empty( $post_type ) ) ? '?post_type=' . $post_type : '';
+	}
 } else {
 	$sendback = remove_query_arg( array('trashed', 'untrashed', 'deleted', 'ids'), $sendback );
 }
@@ -148,6 +152,10 @@
 		$parent_file = "edit.php";
 		$submenu_file = "edit.php";
 		$post_new_file = "post-new.php";
+	} elseif ( 'attachment' == $post_type ) {
+		$parent_file = 'upload.php';
+		$submenu_file = 'upload.php';
+		$post_new_file = 'media-new.php';
 	} else {
 		if ( isset( $post_type_object ) && $post_type_object->show_in_menu && $post_type_object->show_in_menu !== true )
 			$parent_file = $post_type_object->show_in_menu;
Index: wp-admin/edit-form-advanced.php
===================================================================
--- wp-admin/edit-form-advanced.php	(revision 21940)
+++ wp-admin/edit-form-advanced.php	(working copy)
@@ -24,6 +24,11 @@
 	add_action( 'admin_footer', 'wp_print_media_templates' );
 }
 
+if ( post_type_supports( $post_type, 'image-editor' ) ) {
+	wp_enqueue_script( 'image-edit' );
+	wp_enqueue_style( 'imgareaselect' );
+}
+
 /**
  * Post ID global
  * @name $post_ID
@@ -77,7 +82,7 @@
 
 $notice = false;
 $form_extra = '';
-if ( 'auto-draft' == $post->post_status ) {
+if ( 'auto-draft' == get_post_status( $post ) ) {
 	if ( 'edit' == $action )
 		$post->post_title = '';
 	$autosave = false;
@@ -106,13 +111,16 @@
 // All meta boxes should be defined and added before the first do_meta_boxes() call (or potentially during the do_meta_boxes action).
 require_once('./includes/meta-boxes.php');
 
-add_meta_box('submitdiv', __('Publish'), 'post_submit_meta_box', null, 'side', 'core');
+if ( 'attachment' == $post_type )
+	add_meta_box('submitdiv', __('Publish'), 'attachment_submit_meta_box', null, 'side', 'core');
+else
+	add_meta_box('submitdiv', __('Publish'), 'post_submit_meta_box', null, 'side', 'core');
 
 if ( current_theme_supports( 'post-formats' ) && post_type_supports( $post_type, 'post-formats' ) )
 	add_meta_box( 'formatdiv', _x( 'Format', 'post format' ), 'post_format_meta_box', null, 'side', 'core' );
 
 // all taxonomies
-foreach ( get_object_taxonomies($post_type) as $tax_name ) {
+foreach ( get_object_taxonomies($post) as $tax_name ) {
 	$taxonomy = get_taxonomy($tax_name);
 	if ( ! $taxonomy->show_ui )
 		continue;
@@ -125,6 +133,9 @@
 		add_meta_box($tax_name . 'div', $label, 'post_categories_meta_box', null, 'side', 'core', array( 'taxonomy' => $tax_name ));
 }
 
+if ( 'attachment' == $post_type )
+	add_meta_box( 'attachmentdata', __('Attachment Data'), 'attachment_data_meta_box', null, 'normal', 'core' );
+
 if ( post_type_supports($post_type, 'page-attributes') )
 	add_meta_box('pageparentdiv', 'page' == $post_type ? __('Page Attributes') : __('Attributes'), 'page_attributes_meta_box', null, 'side', 'core');
 
@@ -144,10 +155,10 @@
 if ( post_type_supports($post_type, 'comments') )
 	add_meta_box('commentstatusdiv', __('Discussion'), 'post_comment_status_meta_box', null, 'normal', 'core');
 
-if ( ('publish' == $post->post_status || 'private' == $post->post_status) && post_type_supports($post_type, 'comments') )
+if ( ( 'publish' == get_post_status( $post ) || 'private' == get_post_status( $post ) ) && post_type_supports($post_type, 'comments') )
 	add_meta_box('commentsdiv', __('Comments'), 'post_comment_meta_box', null, 'normal', 'core');
 
-if ( !( 'pending' == $post->post_status && !current_user_can( $post_type_object->cap->publish_posts ) ) )
+if ( ! ( 'pending' == get_post_status( $post ) && ! current_user_can( $post_type_object->cap->publish_posts ) ) )
 	add_meta_box('slugdiv', __('Slug'), 'post_slug_meta_box', null, 'normal', 'core');
 
 if ( post_type_supports($post_type, 'author') ) {
@@ -158,6 +169,9 @@
 if ( post_type_supports($post_type, 'revisions') && 0 < $post_ID && wp_get_post_revisions( $post_ID ) )
 	add_meta_box('revisionsdiv', __('Revisions'), 'post_revisions_meta_box', null, 'normal', 'core');
 
+if ( post_type_supports( $post_type, 'image-editor' ) )
+	add_action( 'edit_form_advanced_before', 'edit_form_image_editor' );
+
 do_action('add_meta_boxes', $post_type, $post);
 do_action('add_meta_boxes_' . $post_type, $post);
 
@@ -269,7 +283,7 @@
 <input type="hidden" id="active_post_lock" value="<?php echo esc_attr( implode( ':', $active_post_lock ) ); ?>" />
 <?php
 }
-if ( 'draft' != $post->post_status )
+if ( 'draft' != get_post_status( $post ) )
 	wp_original_referer_field(true, 'previous');
 
 echo $form_extra;
@@ -283,37 +297,45 @@
 
 <div id="post-body" class="metabox-holder columns-<?php echo 1 == get_current_screen()->get_columns() ? '1' : '2'; ?>">
 <div id="post-body-content">
-<?php if ( post_type_supports($post_type, 'title') ) { ?>
+<?php
+do_action( 'edit_form_advanced_before' );
+
+if ( post_type_supports($post_type, 'title') ) {
+?>
 <div id="titlediv">
 <div id="titlewrap">
 	<label class="screen-reader-text" id="title-prompt-text" for="title"><?php echo apply_filters( 'enter_title_here', __( 'Enter title here' ), $post ); ?></label>
 	<input type="text" name="post_title" size="30" value="<?php echo esc_attr( htmlspecialchars( $post->post_title ) ); ?>" id="title" autocomplete="off" />
 </div>
 <div class="inside">
-<?php
-$sample_permalink_html = $post_type_object->public ? get_sample_permalink_html($post->ID) : '';
-$shortlink = wp_get_shortlink($post->ID, 'post');
-if ( !empty($shortlink) )
-    $sample_permalink_html .= '<input id="shortlink" type="hidden" value="' . esc_attr($shortlink) . '" /><a href="#" class="button button-tiny" onclick="prompt(&#39;URL:&#39;, jQuery(\'#shortlink\').val()); return false;">' . __('Get Shortlink') . '</a>';
+	<?php
+	$sample_permalink_html = $post_type_object->public ? get_sample_permalink_html($post->ID) : '';
+	$shortlink = wp_get_shortlink($post->ID, 'post');
+	if ( !empty($shortlink) )
+	    $sample_permalink_html .= '<input id="shortlink" type="hidden" value="' . esc_attr($shortlink) . '" /><a href="#" class="button button-tiny" onclick="prompt(&#39;URL:&#39;, jQuery(\'#shortlink\').val()); return false;">' . __('Get Shortlink') . '</a>';
 
-if ( $post_type_object->public && ! ( 'pending' == $post->post_status && !current_user_can( $post_type_object->cap->publish_posts ) ) ) { ?>
-	<div id="edit-slug-box">
+	if ( $post_type_object->public && ! ( 'pending' == get_post_status( $post ) && !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' != get_post_status( $post ) )
+				echo $sample_permalink_html;
+		?>
+		</div>
 	<?php
-		if ( ! empty($post->ID) && ! empty($sample_permalink_html) && 'auto-draft' != $post->post_status )
-			echo $sample_permalink_html;
-	?>
-	</div>
-<?php
-}
+	}
 ?>
 </div>
 <?php
 wp_nonce_field( 'samplepermalink', 'samplepermalinknonce', false );
 ?>
-</div>
-<?php } ?>
+</div><!-- /titlediv -->
+<?php
+}
 
-<?php if ( post_type_supports($post_type, 'editor') ) { ?>
+do_action( 'edit_form_after_title' );
+
+if ( post_type_supports($post_type, 'editor') ) {
+?>
 <div id="postdivrich" class="postarea">
 
 <?php wp_editor($post->post_content, 'content', array('dfw' => true, 'tabfocus_elements' => 'sample-permalink,post-preview') ); ?>
@@ -323,7 +345,7 @@
 	<td class="autosave-info">
 	<span class="autosave-message">&nbsp;</span>
 <?php
-	if ( 'auto-draft' != $post->post_status ) {
+	if ( 'auto-draft' != get_post_status( $post ) ) {
 		echo '<span id="last-edit">';
 		if ( $last_id = get_post_meta($post_ID, '_edit_last', true) ) {
 			$last_user = get_userdata($last_id);
@@ -337,7 +359,11 @@
 </tr></tbody></table>
 
 </div>
-<?php } ?>
+<?php
+}
+
+do_action( 'edit_form_after_editor' );
+?>
 </div><!-- /post-body-content -->
 
 <div id="postbox-container-1" class="postbox-container">
Index: wp-admin/menu.php
===================================================================
--- wp-admin/menu.php	(revision 21940)
+++ wp-admin/menu.php	(working copy)
@@ -64,7 +64,14 @@
 	$submenu['upload.php'][5] = array( __('Library'), 'upload_files', 'upload.php');
 	/* translators: add new file */
 	$submenu['upload.php'][10] = array( _x('Add New', 'file'), 'upload_files', 'media-new.php');
+	foreach ( get_taxonomies_for_attachments( 'objects' ) as $tax ) {
+		if ( ! $tax->show_ui )
+			continue;
 
+		$submenu['upload.php'][$i++] = array( esc_attr( $tax->labels->menu_name ), $tax->cap->manage_terms, 'edit-tags.php?taxonomy=' . $tax->name . '&amp;post_type=attachment' );
+	}
+	unset($tax);
+
 $menu[15] = array( __('Links'), 'manage_links', 'link-manager.php', '', 'menu-top menu-icon-links', 'menu-links', 'none' );
 	$submenu['link-manager.php'][5] = array( _x('All Links', 'admin menu'), 'manage_links', 'link-manager.php' );
 	/* translators: add new links */
