Index: wp-includes/class-wp-customize.php
===================================================================
--- wp-includes/class-wp-customize.php	(revision 19998)
+++ wp-includes/class-wp-customize.php	(working copy)
@@ -81,7 +81,6 @@
 
 		add_filter( 'template', array( $this, 'get_template' ) );
 		add_filter( 'stylesheet', array( $this, 'get_stylesheet' ) );
-		add_filter( 'pre_option_current_theme', array( $this, 'current_theme' ) );
 
 		// @link: http://core.trac.wordpress.org/ticket/20027
 		add_filter( 'pre_option_stylesheet', array( $this, 'get_stylesheet' ) );
@@ -235,27 +234,6 @@
 	}
 
 	/**
-	 * Filter the current theme and return the name of the previewed theme.
-	 *
-	 * @since 3.4.0
-	 *
-	 * @return string Theme name.
-	 */
-	public function current_theme( $current_theme ) {
-		$themes = get_themes();
-
-		if ( ! $themes )
-			return $current_theme;
-
-		foreach ( $themes as $theme ) {
-			if ( $theme['Stylesheet'] == $this->stylesheet && $theme['Template'] == $this->template )
-				return $theme['Name'];
-		}
-
-		return $current_theme;
-	}
-
-	/**
 	 * Trigger save action and load customize controls.
 	 *
 	 * @since 3.4.0
Index: wp-includes/update.php
===================================================================
--- wp-includes/update.php	(revision 19998)
+++ wp-includes/update.php	(working copy)
@@ -235,32 +235,29 @@
 	if ( defined( 'WP_INSTALLING' ) )
 		return false;
 
-	if ( !function_exists( 'get_themes' ) )
-		require_once( ABSPATH . 'wp-includes/theme.php' );
-
-	$installed_themes = get_themes( );
+	$installed_themes = wp_get_themes();
 	$last_update = get_site_transient( 'update_themes' );
 	if ( ! is_object($last_update) )
 		$last_update = new stdClass;
 
 	$themes = array();
 	$checked = array();
-	$exclude_fields = array('Template Files', 'Stylesheet Files', 'Status', 'Theme Root', 'Theme Root URI', 'Template Dir', 'Stylesheet Dir', 'Description', 'Tags', 'Screenshot');
 
 	// Put slug of current theme into request.
 	$themes['current_theme'] = get_option( 'stylesheet' );
 
-	foreach ( (array) $installed_themes as $theme_title => $theme ) {
-		$themes[$theme['Stylesheet']] = array();
-		$checked[$theme['Stylesheet']] = $theme['Version'];
+	foreach ( $installed_themes as $theme ) {
+		$checked[ $theme->get_stylesheet() ] = $theme->get('Version');
 
-		$themes[$theme['Stylesheet']]['Name'] = $theme['Name'];
-		$themes[$theme['Stylesheet']]['Version'] = $theme['Version'];
-
-		foreach ( (array) $theme as $key => $value ) {
-			if ( !in_array($key, $exclude_fields) )
-				$themes[$theme['Stylesheet']][$key] = $value;
-		}
+		$themes[ $theme->get_stylesheet() ] = array(
+			'Name'       => $theme->get('Name'),
+			'Title'      => $theme->get('Name'),
+			'Version'    => $theme->get('Version'),
+			'Author'     => $theme->get('Author'),
+			'Author URI'  => $theme->get('AuthorURI'),
+			'Template'   => $theme->get_template(),
+			'Stylesheet' => $theme->get_stylesheet(),
+		);
 	}
 
 	// Check for update on a different schedule, depending on the page.
Index: wp-includes/class-wp-theme.php
===================================================================
--- wp-includes/class-wp-theme.php	(revision 0)
+++ wp-includes/class-wp-theme.php	(revision 0)
@@ -0,0 +1,1175 @@
+<?php
+/**
+ * WP_Theme Class
+ *
+ * @package WordPress
+ * @subpackage Theme
+ */
+
+final class WP_Theme implements ArrayAccess {
+
+	/**
+	 * Headers for style.css files.
+	 *
+	 * @static
+	 * @access private
+	 * @var array
+	 */
+	private static $file_headers = array(
+		'Name'        => 'Theme Name',
+		'ThemeURI'    => 'Theme URI',
+		'Description' => 'Description',
+		'Author'      => 'Author',
+		'AuthorURI'   => 'Author URI',
+		'Version'     => 'Version',
+		'Template'    => 'Template',
+		'Status'      => 'Status',
+		'Tags'        => 'Tags',
+		'TextDomain'  => 'Text Domain',
+		'DomainPath'  => 'Domain Path',
+	);
+
+	/**
+	 * Absolute path to the theme root, usually wp-content/themes
+	 *
+	 * @access private
+	 * @var string
+	 */
+	private $theme_root;
+
+	/**
+	 * Header data from the theme's style.css file.
+	 *
+	 * @access private
+	 * @var array
+	 */
+	private $headers = array();
+
+	/**
+	 * Header data from the theme's style.css file after being sanitized.
+	 *
+	 * @access private
+	 * @var array
+	 */
+	private $headers_sanitized;
+
+	/**
+	 * Header name from the theme's style.css after being translated.
+	 *
+	 * Cached due to sorting functions running over the translated name.
+	 */
+	private $name_translated;
+
+	/**
+	 * Errors encountered when initializing the theme.
+	 *
+	 * @access private
+	 * @var WP_Error
+	 */
+	private $errors;
+
+	/**
+	 * The directory name of the theme's files, inside the theme root.
+	 *
+	 * In the case of a child theme, this is directory name of the the child theme.
+	 * Otherwise, 'stylesheet' is the same as 'template'.
+	 *
+	 * @access private
+	 * @var string
+	 */
+	private $stylesheet;
+
+	/**
+	 * The directory name of the theme's files, inside the theme root.
+	 *
+	 * In the case of a child theme, this is the directory name of the parent theme.
+	 * Otherwise, 'template' is the same as 'stylesheet'.
+	 *
+	 * @access private
+	 * @var string
+	 */
+	private $template;
+
+	/**
+	 * A reference to the parent theme, in the case of a child theme.
+	 *
+	 * @access private
+	 * @var WP_Theme
+	 */
+	private $parent;
+
+	/**
+	 * Flag for whether the theme's textdomain is loaded.
+	 *
+	 * @access private
+	 * @var bool
+	 */
+	private $textdomain_loaded;
+
+	/**
+	 * Flag for whether the themes cache bucket should be persistently cached.
+	 *
+	 * Default is false. Can be set with the wp_cache_themes_persistent filter.
+	 *
+	 * @access private
+	 * @var bool
+	 */
+	private static $persistently_cache;
+
+	/**
+	 * Expiration time for the themes cache bucket.
+	 *
+	 * By default the bucket is not cached, so this value is useless.
+	 *
+	 * @access private
+	 * @var bool
+	 */
+	private static $cache_expiration = 7200;
+
+	/**
+	 * Constructor for WP_Theme.
+	 *
+	 * @param string $theme_dir Directory of the theme within the theme_root.
+	 * @param string $theme_root Theme root.
+	 * @param WP_Error|null $child If this theme is a parent theme, the child may be passed for validation purposes.
+	 */
+	public function __construct( $theme_dir, $theme_root, $child = null ) {
+		// Initialize caching on first run.
+		if ( ! isset( self::$persistently_cache ) ) {
+			self::$persistently_cache = apply_filters( 'wp_cache_themes_persistently', false );
+			if ( is_int( self::$persistently_cache ) )
+				self::$cache_expiration = self::$persistently_cache;
+			elseif ( ! self::$persistently_cache )
+				wp_cache_add_non_persistent_groups( 'themes' );
+		}
+
+		$this->theme_root = $theme_root;
+		$this->stylesheet = $theme_dir;
+		$theme_file = $this->stylesheet . '/style.css';
+
+		$cache = $this->cache_get( 'theme' );
+
+		if ( is_array( $cache ) ) {
+			foreach ( array( 'errors', 'headers', 'template', 'stylesheet' ) as $key ) {
+				if ( isset( $cache[ $key ] ) )
+					$this->$key = $cache[ $key ];
+			}
+			if ( $this->errors )
+				return;
+		} elseif ( ! file_exists( $this->theme_root . '/' . $theme_file ) ) {
+			$this->headers['Name'] = $this->stylesheet;
+			$this->errors = new WP_Error( 'theme_no_stylesheet', __( 'Stylesheet is missing.' ) );
+			$this->cache_add( 'theme', array( 'headers' => $this->headers, 'errors' => $this->errors, 'stylesheet' => $this->stylesheet ) );
+			if ( ! file_exists( $this->theme_root ) ) // Don't cache this one.
+				$this->errors->add( 'theme_root_missing', __( 'ERROR: The themes directory is either empty or doesn&#8217;t exist. Please check your installation.' ) );
+			return;
+		} elseif ( ! is_readable( $this->theme_root . '/' . $theme_file ) ) {
+			$this->headers['Name'] = $this->stylesheet;
+			$this->errors = new WP_Error( 'theme_stylesheet_not_readable', __( 'Stylesheet is not readable.' ) );
+			$this->cache_add( 'theme', array( 'headers' => $this->headers, 'errors' => $this->errors, 'stylesheet' => $this->stylesheet ) );
+			return;
+		} else {
+			$this->headers = get_file_data( $this->theme_root . '/' . $theme_file, self::$file_headers, 'theme' );
+		}
+
+		if ( ! $this->template = $this->get('Template') ) {
+			if ( file_exists( $this->theme_root . '/' . $this->stylesheet . '/index.php' ) ) {
+				$this->template = $this->stylesheet;
+			} else {
+				$this->errors = new WP_Error( 'theme_no_index', __( 'Template is missing.' ) );
+				$this->cache_add( 'theme', array( 'headers' => $this->headers, 'errors' => $this->errors, 'stylesheet' => $this->stylesheet ) );
+				return;
+			}
+		}
+
+		if ( $this->template != $this->stylesheet && ! file_exists( $this->theme_root . '/' . $this->template . '/index.php' ) ) {
+			// If we're in a directory of themes inside /themes, look for the parent nearby.
+			// wp-content/themes/directory-of-themes/*
+			$parent_dir = dirname( $this->stylesheet );
+			if ( '.' != $parent_dir && file_exists( $this->theme_root . '/' . $parent_dir . '/' . $this->template . '/index.php' ) ) {
+				$this->template = $parent_dir . '/' . $this->template;
+			} elseif ( ( $directories = search_theme_directories() ) && isset( $directories[ $this->template ] ) ) {
+				// Look for the template in the search_theme_directories() results, in case it is in another theme root.
+				// We don't look into directories of themes, just the theme root.
+				$theme_root_template = $directories[ $this->template ]['theme_root'];
+			} else {
+				// Parent theme is missing.
+				$this->errors = new WP_Error( 'theme_no_parent', sprintf( __( 'The parent theme is missing. Please install the "%s" parent theme.' ), $this->template ) );
+				$this->cache_add( 'theme', array( 'headers' => $this->headers, 'errors' => $this->errors, 'stylesheet' => $this->stylesheet, 'template' => $this->template ) );
+				return;
+			}
+		}
+
+		// @TODO Check for theme name collision. But guess what? We don't care anymore! We only care about clashing matches found in search_theme_directories().
+
+		// Set the parent, if we're a child theme.
+		if ( $this->template != $this->stylesheet ) {
+			// If we are a parent, then there is a problem. Only two generations allowed! Cancel things out.
+			if ( is_a( $child, 'WP_Theme' ) && $child->template == $this->stylesheet ) {
+				$child->parent = null;
+				$child->errors = new WP_Error( 'theme_parent_invalid', sprintf( __( 'The "%s" theme is not a valid parent theme.' ), $child->template ) );
+				$child->cache_add( 'theme', array( 'headers' => $child->headers, 'errors' => $child->errors, 'stylesheet' => $child->stylesheet, 'template' => $child->template ) );
+				// The two themes actually reference each other with the Template header.
+				if ( $child->stylesheet == $this->template ) {
+					$this->errors = new WP_Error( 'theme_parent_invalid', sprintf( __( 'The "%s" theme is not a valid parent theme.' ), $this->template ) );
+					$this->cache_add( 'theme', array( 'headers' => $this->headers, 'errors' => $this->errors, 'stylesheet' => $this->stylesheet, 'template' => $this->template ) );
+				}
+				return;
+			}
+			// Set the parent. Pass the current instance so we can do the crazy checks above and assess errors.
+			$this->parent = new WP_Theme( $this->template, isset( $theme_root_template ) ? $theme_root_template : $this->theme_root, $this );
+		}
+
+		// We're good. If we didn't retrieve from cache, set it.
+		if ( ! is_array( $cache ) )
+			$this->cache_add( 'theme', array( 'headers' => $this->headers, 'errors' => $this->errors, 'stylesheet' => $this->stylesheet, 'template' => $this->template ) );
+	}
+
+	/**
+	 * __isset() magic method for properties formerly returned by current_theme_info()
+	 */
+	public function __isset( $offset ) {
+		static $properties = array(
+			'name', 'title', 'version', 'parent_theme', 'template_dir', 'stylesheet_dir', 'template', 'stylesheet',
+			'screenshot', 'description', 'author', 'tags', 'theme_root', 'theme_root_uri',
+		);
+
+		return in_array( $offset, $properties );
+	}
+
+	/**
+	 * __get() magic method for properties formerly returned by current_theme_info()
+	 */
+	public function __get( $offset ) {
+		switch ( $offset ) {
+			case 'name' :
+			case 'title' :
+				return $this->get('Name');
+			case 'version' :
+				return $this->get('Version');
+			case 'parent_theme' :
+				return $this->parent ? $this->parent->get('Name') : '';
+			case 'template_dir' :
+				return $this->get_template_directory();
+			case 'stylesheet_dir' :
+				return $this->get_stylesheet_directory();
+			case 'template' :
+				return $this->get_template();
+			case 'stylesheet' :
+				return $this->get_stylesheet();
+			case 'screenshot' :
+				return $this->get_screenshot();
+			// 'author' and 'description' did not previously return translated data.
+			case 'description' :
+				return $this->display('Description');
+			case 'author' :
+				return $this->display('Author');
+			case 'tags' :
+				return $this->get( 'Tags' );
+			case 'theme_root' :
+				return $this->get_theme_root();
+			case 'theme_root_uri' :
+				return $this->get_theme_root_uri();
+		}
+	}
+
+	/**
+	 * Method to implement ArrayAccess for keys formerly returned by get_themes()
+	 */
+    public function offsetSet( $offset, $value ) {}
+
+	/**
+	 * Method to implement ArrayAccess for keys formerly returned by get_themes()
+	 */
+    public function offsetUnset( $offset ) {}
+
+	/**
+	 * Method to implement ArrayAccess for keys formerly returned by get_themes()
+	 */
+    public function offsetExists( $offset ) {
+    	static $keys = array(
+    		'Name', 'Version', 'Status', 'Title', 'Author', 'Author Name', 'Author URI', 'Description',
+			'Template', 'Stylesheet', 'Template Files', 'Stylesheet Files', 'Template Dir', 'Stylesheet Dir',
+			 'Screenshot', 'Tags', 'Theme Root', 'Theme Root URI', 'Parent Theme',
+		);
+
+    	return in_array( $offset, $keys );
+	}
+
+	/**
+	 * Method to implement ArrayAccess for keys formerly returned by get_themes()
+	 */
+	public function offsetGet( $offset ) {
+    	switch ( $offset ) {
+			case 'Name' :
+			case 'Version' :
+			case 'Status' :
+				return $this->get( $offset );
+			case 'Title' :
+				return $this->get('Name');
+			// Author, Author Name, Author URI, and Description did not
+			// previously return translated data. We are doing so now.
+			// Title and Name could have been used as the key for get_themes(),
+			// so both to remain untranslated for back compatibility.
+			case 'Author' :
+				return $this->display( 'Author');
+			case 'Author Name' :
+				return $this->display( 'Author', false);
+			case 'Author URI' :
+				return $this->display('AuthorURI');
+			case 'Description' :
+				return $this->display( 'Description');
+			case 'Template' :
+				return $this->get_template();
+			case 'Stylesheet' :
+				return $this->get_stylesheet();
+			case 'Template Files' :
+				$files = $this->get_files('php');
+				foreach ( $files as &$file )
+					$file = $this->theme_root . '/' . $file;
+				return $files;
+			case 'Stylesheet Files' :
+				$files = $this->get_files('css');
+				foreach ( $files as &$file )
+					$file = $this->theme_root . '/' . $file;
+				return $files;
+			case 'Template Dir' :
+				return $this->get_template_directory();
+			case 'Stylesheet Dir' :
+				return $this->get_stylesheet_directory();
+			case 'Screenshot' :
+				return $this->get_screenshot();
+			case 'Tags' :
+				return $this->get('Tags');
+			case 'Theme Root' :
+				return $this->get_theme_root();
+			case 'Theme Root URI' :
+				return $this->get_theme_root_uri();
+			case 'Parent Theme' :
+				return $this->parent ? $this->parent->get('Name') : '';
+			default :
+				return null;
+		}
+	}
+
+	/**
+	 * Returns errors property.
+	 *
+	 * @since 3.4.0
+	 * @access public
+	 *
+	 * @return WP_Error|bool WP_Error if there are errors, or false.
+	 */
+	public function errors() {
+		return is_wp_error( $this->errors ) ? $this->errors : false;
+	}
+
+	/**
+	 * Returns reference to the parent theme.
+	 *
+	 * @since 3.4.0
+	 * @access public
+	 *
+	 * @return WP_Theme|bool Parent theme, or false if the current theme is not a child theme.
+	 */
+	public function parent() {
+		return isset( $this->parent ) ? $this->parent : false;
+	}
+
+	/**
+	 * Adds theme data to cache.
+	 *
+	 * Cache entries keyed by the theme and the type of data.
+	 *
+	 * @access private
+	 * @since 3.4.0
+	 *
+	 * @param string $key Type of data to store (theme, screenshot, screenshot_count, files, headers)
+	 * @param string $data Data to store
+	 * @return bool Return value from wp_cache_add()
+	 */
+	private function cache_add( $key, $data ) {
+		return wp_cache_add( $key . '-' . $this->theme_root . '/' . $this->stylesheet, $data, 'themes', self::$cache_expiration );
+	}
+
+	/**
+	 * Gets theme data from cache.
+	 *
+	 * Cache entries are keyed by the theme and the type of data.
+	 *
+	 * @access private
+	 * @since 3.4.0
+	 *
+	 * @param string $key Type of data to retrieve (theme, screenshot, screenshot_count, files, headers)
+	 * @return mixed Retrieved data
+	 */
+	private function cache_get( $key ) {
+		return wp_cache_get( $key . '-' . $this->theme_root . '/' . $this->stylesheet, 'themes' );
+	}
+
+	/**
+	 * Clears the cache for the theme.
+	 *
+	 * @access public
+	 * @since 3.4.0
+	 */
+	public function clear_cache() {
+		wp_cache_delete( 'theme-' . $this->cache_key, 'themes' );
+		wp_cache_delete( 'screenshot-' . $this->cache_key, 'themes' );
+		wp_cache_delete( 'screenshot_count-' . $this->cache_key, 'themes' );
+		wp_cache_delete( 'files-' . $this->cache_key, 'themes' );
+		wp_cache_delete( 'headers-' . $this->cache_key, 'themes' );
+	}
+
+	/**
+	 * Gets a theme header.
+	 *
+	 * The header is sanitized.
+	 *
+	 * @access public
+	 * @since 3.4.0
+	 *
+	 * @param string $header Theme header. Name, Description, Author, Version, ThemeURI, AuthorURI, Status.
+	 * @return string String on success, false on failure.
+	 */
+	public function get( $header ) {
+		if ( ! isset( $this->headers[ $header ] ) )
+			return false;
+
+		if ( ! isset( $this->headers_sanitized ) ) {
+			$this->headers_sanitized = $this->cache_get( 'headers' );
+			if ( ! is_array( $this->headers_sanitized ) )
+				$headers = array();
+		}
+
+		if ( isset( $this->headers_sanitized[ $header ] ) )
+			return $this->headers_sanitized[ $header ];
+
+		// If an external object cache does not consider themes to be a persistent group, sanitize everything and cache it.
+		if ( self::$persistently_cache ) {
+			foreach ( array_keys( $this->headers ) as $header )
+				$this->headers_sanitized[ $header ] = $this->sanitize_header( $header, $this->headers[ $header ] );
+			$this->cache_add( 'headers', $this->headers_sanitized );
+		} else {
+			$this->headers_sanitized[ $header ] = $this->sanitize_header( $header, $this->headers[ $header ] );
+		}
+
+		return $this->headers_sanitized[ $header ];
+	}
+
+	/**
+	 * Gets a theme header ready for display (marked up, translated).
+	 *
+	 * @access public
+	 * @since 3.4.0
+	 *
+	 * @param string $header Theme header. Name, Description, Author, Version, ThemeURI, AuthorURI, Status.
+	 * @param bool $markup Optional. Whether to mark up the header. Defaults to true.
+	 * @param bool $translate Optional. Whether to translate the header. Defaults to true.
+	 * @return string Processed header, false on failure.
+	 */
+	public function display( $header, $markup = true, $translate = true ) {
+		$value = $this->get( $header );
+		if ( false === $value || '' === $value )
+			return $value;
+
+		if ( ! $this->load_textdomain() )
+			$translate = false;
+
+		if ( $translate )
+			$value = $this->translate_header( $header, $value );
+
+		if ( $markup )
+			$value = $this->markup_header( $header, $value, $translate );
+
+		return $value;
+	}
+
+	/**
+	 * Sanitize a theme header.
+	 *
+	 * @param string $header Theme header. Name, Description, Author, Version, ThemeURI, AuthorURI, Status.
+	 * @param string $value Value to sanitize.
+	 */
+	private function sanitize_header( $header, $value ) {
+		switch ( $header ) {
+			case 'Status' :
+				if ( ! $value ) {
+					$value = 'public';
+					break;
+				}
+				// Fall through otherwise.
+			case 'Name' :
+			case 'Author' :
+				static $header_tags = array(
+					'abbr'    => array( 'title' => true ),
+					'acronym' => array( 'title' => true ),
+					'code'    => true,
+					'em'      => true,
+					'strong'  => true,
+				);
+				$value = wp_kses( $value, $header_tags );
+				break;
+			case 'Description' :
+				static $header_tags_with_a = array(
+					'a'       => array( 'href' => true, 'title' => true ),
+					'abbr'    => array( 'title' => true ),
+					'acronym' => array( 'title' => true ),
+					'code'    => true,
+					'em'      => true,
+					'strong'  => true,
+				);
+				$value = wp_kses( $value, $header_tags_with_a );
+				break;
+			case 'ThemeURI' :
+			case 'AuthorURI' :
+				$value = esc_url( $value );
+				break;
+			case 'Tags' :
+				$value = array_filter( array_map( 'trim', explode( ',', strip_tags( $value ) ) ) );
+				break;
+		}
+
+		return $value;
+	}
+
+	/**
+	 * Mark up a theme header.
+	 *
+	 * @access private
+	 * @since 3.4.0
+	 *
+	 * @param string $header Theme header. Name, Description, Author, Version, ThemeURI, AuthorURI, Status.
+	 * @param string $value Value to mark up.
+	 * @param string $translate Whether the header has been translated.
+	 * @return string Value, marked up.
+	 */
+	private function markup_header( $header, $value, $translate ) {
+		switch ( $header ) {
+			case 'Description' :
+				$value = wptexturize( $value );
+				break;
+			case 'Author' :
+				if ( $this->get('AuthorURI') ) {
+					static $attr = null;
+					if ( ! isset( $attr ) )
+						$attr = esc_attr__( 'Visit author homepage' );
+					$value = sprintf( '<a href="%1$s" title="%2$s">%3$s</a>', $this->display( 'AuthorURI', true, $translate ), $attr, $value );
+				} elseif ( ! $value ) {
+					$value = __( 'Anonymous' );
+				}
+				break;
+			case 'Tags' :
+				static $comma = null;
+				if ( ! isset( $comma ) ) {
+					/* translators: used between list items, there is a space after the comma */
+					$comma = __( ', ' );
+				}
+				$value = implode( $comma, $value );
+				break;
+		}
+
+		return $value;
+	}
+
+	/**
+	 * Translate a theme header.
+	 *
+	 * @access private
+	 * @since 3.4.0
+	 *
+	 * @param string $header Theme header. Name, Description, Author, Version, ThemeURI, AuthorURI, Status.
+	 * @param string $value Value to translate.
+	 * @return string Translated value.
+	 */
+	private function translate_header( $header, $value ) {
+		switch ( $header ) {
+			case 'Name' :
+				// Cached for sorting reasons.
+				if ( isset( $this->name_translated ) )
+					return $this->name_translated;
+				$this->name_translated = translate( $value, $this->get('TextDomain' ) );
+				return $this->name_translated;
+			case 'Tags' :
+				if ( empty( $value ) )
+					return $value;
+
+				static $tags_list;
+				if ( ! isset( $tags_list ) ) {
+					$tags_list = array();
+					$feature_list = get_theme_feature_list( false ); // No API
+					foreach ( $feature_list as $tags )
+						$tags_list += $tags;
+				}
+
+				foreach ( $value as &$tag ) {
+					if ( isset( $tags_list[ $tag ] ) )
+						$tag = $tags_list[ $tag ];
+				}
+
+				return $value;
+				break;
+			default :
+				$value = translate( $value, $this->get('TextDomain') );
+		}
+		return $value;
+	}
+
+	/**
+	 * The directory name of the theme's "stylesheet" files, inside the theme root.
+	 *
+	 * In the case of a child theme, this is directory name of the the child theme.
+	 * Otherwise, get_stylesheet() is the same as get_template().
+	 *
+	 * @since 3.4.0
+	 * @access public
+	 *
+	 * @return string Stylesheet
+	 */
+	public function get_stylesheet() {
+		return $this->stylesheet;
+	}
+
+	/**
+	 * The directory name of the theme's "template" files, inside the theme root.
+	 *
+	 * In the case of a child theme, this is the directory name of the parent theme.
+	 * Otherwise, the get_template() is the same as get_stylesheet().
+	 *
+	 * @since 3.4.0
+	 * @access public
+	 *
+	 * @return string Template
+	 */
+	public function get_template() {
+		return $this->template;
+	}
+
+	/**
+	 * Whether a theme is a child theme.
+	 *
+	 * @since 3.4.0
+	 * @access public
+	 *
+	 * @return bool True if a theme is a child theme, false otherwise.
+	 */
+	public function is_child_theme() {
+		return $this->template !== $this->stylesheet;
+	}
+
+	/**
+	 * Returns the absolute path to the directory of a theme's "stylesheet" files.
+	 *
+	 * In the case of a child theme, this is the absolute path to the directory
+	 * of the child theme's files.
+	 *
+	 * @since 3.4.0
+	 * @access public
+	 *
+	 * @return string Absolute path of the stylesheet directory.
+	 */
+	public function get_stylesheet_directory() {
+		if ( $this->errors && in_array( 'theme_root_missing', $this->errors->get_error_codes() ) )
+			return '';
+
+		return $this->theme_root . '/' . $this->stylesheet;
+	}
+
+	/**
+	 * Returns the absolute path to the directory of a theme's "template" files.
+	 *
+	 * In the case of a child theme, this is the absolute path to the directory
+	 * of the parent theme's files.
+	 *
+	 * @since 3.4.0
+	 * @access public
+	 *
+	 * @return string Absolute path of the template directory.
+	 */
+	public function get_template_directory() {
+		if ( $this->parent )
+			$theme_root = $this->parent->theme_root;
+		else
+			$theme_root = $this->theme_root;
+
+		return $theme_root . '/' . $this->template;
+	}
+
+	/**
+	 * Returns the URL to the directory of a theme's "stylesheet" files.
+	 *
+	 * In the case of a child theme, this is the URL to the directory of the
+	 * child theme's files.
+	 *
+	 * @since 3.4.0
+	 * @access public
+	 *
+	 * @return string URL to the stylesheet directory.
+	 */
+	public function get_stylesheet_directory_uri() {
+		return $this->theme_root_uri . '/' . $this->stylesheet;
+	}
+
+	/**
+	 * Returns the URL to the directory of a theme's "template" files.
+	 *
+	 * In the case of a child theme, this is the URL to the directory of the
+	 * parent theme's files.
+	 *
+	 * @since 3.4.0
+	 * @access public
+	 *
+	 * @return string URL to the template directory.
+	 */
+	public function get_template_directory_uri() {
+		if ( $this->parent )
+			$theme_root_uri = $this->parent->theme_root_uri;
+		else
+			$theme_root_uri = $this->theme_root;
+
+		return $theme_root . '/' . $this->template;
+	}
+
+	/**
+	 * The absolute path to the directory of the theme root.
+	 *
+	 * This is typically the absolute path to wp-content/themes.
+	 *
+	 * @since 3.4.0
+	 * @access public
+	 *
+	 * @return string Theme root.
+	 */
+	public function get_theme_root() {
+		return $this->theme_root;
+	}
+
+	/**
+	 * Returns the URL to the directory of the theme root.
+	 *
+	 * This is typically the absolute path to wp-content/themes.
+	 *
+	 * @since 3.4.0
+	 * @access public
+	 *
+	 * @return string Theme root URI.
+	 */
+	public function get_theme_root_uri() {
+		return str_replace( WP_CONTENT_DIR, content_url(), $this->theme_root );
+	}
+
+	/**
+	 * Returns the main screenshot file for the theme.
+	 *
+	 * The main screenshot is called screenshot.png. gif and jpg extensions are also allowed.
+	 *
+	 * Screenshots for a theme must be in the stylesheet directory. (In the case of a child
+	 * theme, a parent theme's screenshots are inherited.)
+	 *
+	 * @since 3.4.0
+	 * @access public
+	 *
+	 * @param string $uri Type of URL to include, either relative or absolute. Defaults to relative.
+	 * @return mixed Screenshot file. False if the theme does not have a screenshot.
+	 */
+	public function get_screenshot( $uri = 'relative' ) {
+		$screenshot = $this->cache_get( 'screenshot' );
+		if ( $screenshot ) {
+			if ( 'absolute' == $uri )
+				return $this->get_stylesheet_directory_uri() . '/' . $screenshot;
+			return $screenshot;
+		} elseif ( 0 === $screenshot ) {
+			return false;
+		}
+
+		foreach ( array( 'png', 'gif', 'jpg', 'jpeg' ) as $ext ) {
+			if ( file_exists( $this->get_stylesheet_directory() . "/screenshot.$ext" ) ) {
+				$this->cache_add( 'screenshot', 'screenshot.' . $ext );
+				if ( 'absolute' == $uri )
+					return $this->get_stylesheet_directory_uri() . '/' . 'screenshot.' . $ext;
+				return 'screenshot.' . $ext;
+			}
+		}
+
+		$this->cache_add( 'screenshot', 0 );
+		$this->cache_add( 'screenshot_count', 0 );
+		return false;
+	}
+
+	/**
+	 * Returns the number of screenshots for a theme.
+	 *
+	 * The first screenshot may be called screenshot.png, .gif, or .jpg. Subsequent
+	 * screenshots can be screenshot-2.png, screenshot-3.png, etc. The count must
+	 * be consecutive for screenshots to be counted, and all screenshots beyond the
+	 * initial one must be image/png files.
+	 *
+	 * @see WP_Theme::get_screenshot()
+	 * @since 3.4.0
+	 * @access public
+	 *
+	 * @return int Number of screenshots. Can be 0.
+	 */
+	public function get_screenshot_count() {
+		$screenshot_count = $this->cache_get( 'screenshot_count' );
+		if ( is_numeric( $screenshot_count ) )
+			return $screenshot_count;
+
+		// This will set the screenshot cache.
+		// If there is no screenshot, the screenshot_count cache will also be set.
+		if ( ! $screenshot = $this->get_screenshot() )
+			return 0;
+
+		$prefix = $this->get_stylesheet() . '/screenshot-';
+		$files = self::scandir( $this->get_stylesheet_directory(), $this->get_stylesheet(), 'png', 0 );
+
+		$screenshot_count = 1;
+		while ( in_array( $prefix . ( $screenshot_count + 1 ) . '.png', $files['png'] ) )
+			$screenshot_count++;
+
+		$this->cache_add( 'screenshot_count', $screenshot_count );
+		return $screenshot_count;
+	}
+
+	/**
+	 * Returns an array of screenshot filenames.
+	 *
+	 * @see WP_Theme::get_screenshot()
+	 * @see WP_Theme::get_screenshot_count()
+	 * @since 3.4.0
+	 * @access public
+	 *
+	 * @return array Screenshots.
+	 */
+	public function get_screenshots() {
+		if ( ! $count = $this->get_screenshot_count() )
+			return array();
+
+		$screenshots = array( $this->get_screenshot() );
+		for ( $i = 2; $i <= $count; $i++ )
+			$screenshots[] = 'screenshot-' . $i . '.png';
+		return $screenshots;
+	}
+
+	/**
+	 * Return files in the template and stylesheet directories.
+	 *
+	 * @since 3.4.0
+	 * @access public
+	 *
+	 * @param string|null $type Optional. Type of files to return, either 'php' or 'css'. Defaults to null, for both.
+	 * @return array If a specific $type is requested, returns an array of PHP files. If no $type is requested,
+	 * 	returns an array, with the keys being the file types, and the values being an array of files for those type.
+	 */
+	public function get_files( $type = null, $include_parent_files = false ) {
+		$files = $this->cache_get( 'files' );
+		if ( ! is_array( $files ) ) {
+			if ( $include_parent_files || ! $this->is_child_theme() )
+				// Template files can be one level down for the purposes of the theme editor, so this should be $depth = 1.
+				// Todo: We ignore this for now, but this is why the branching is weird.
+				$files = (array) self::scandir( $this->get_template_directory(), $this->get_template(), array( 'php', 'css' ) );
+			if ( $this->is_child_theme() )
+				$files = array_merge_recursive( $files, (array) self::scandir( $this->get_stylesheet_directory(), $this->get_stylesheet(), array( 'php', 'css' ) ) );
+			foreach ( $files as &$group )
+				sort( $group );
+			$this->cache_add( 'files', $files );
+		}
+
+		if ( null === $type )
+			return $files;
+		elseif ( isset( $files[ $type ] ) )
+			return $files[ $type ];
+
+		return array();
+	}
+
+	public function get_page_templates() {
+		// If you screw up your current theme and we invalidate your parent, most things still work. Let it slide.
+		if ( $this->errors() && $this->errors()->get_error_codes() !== array( 'theme_parent_invalid' ) )
+			return array();
+
+		$page_templates = $this->cache_get( 'page_templates' );
+		if ( is_array( $page_templates ) )
+			return $page_templates;
+		$page_templates = array();
+
+		$files = (array) self::scandir( $this->get_template_directory(), $this->get_template_directory(), 'php' );
+		if ( $this->is_child_theme() )
+			$files = array_merge_recursive( $files, (array) self::scandir( $this->get_stylesheet_directory(), $this->get_stylesheet_directory(), 'php' ) );
+
+		foreach ( $files['php'] as $file ) {
+			$headers = get_file_data( $file, array( 'Name' => 'Template Name' ) );
+			if ( empty( $headers['Name'] ) )
+				continue;
+			$page_templates[ $headers['Name'] ] = basename( $file );
+		}
+
+		$this->cache_add( 'page_templates', $page_templates );
+		return $page_templates;
+	}
+
+	/**
+	 * Scans a directory for files of a certain extension.
+	 *
+	 * @since 3.4.0
+	 * @access public
+	 *
+	 * @param string $path Absolute path to search.
+	 * @param string $relative_path The basename of the absolute path. Used to control the returned path
+	 * 	for the found files, particularly when this function recurses to lower depths.
+	 * @param array|string $extensions Array of extensions to find, or string of a single extension.
+	 * @depth int How deep to search for files. Optional, defaults to a flat scan (0 depth).
+	 */
+	private static function scandir( $path, $relative_path, $extensions, $depth = 0 ) {
+		if ( is_array( $extensions ) )
+			$extensions = implode( '|', $extensions );
+
+		if ( ! is_dir( $path ) )
+			return false;
+
+		$results = scandir( $path );
+		$files = array();
+
+		foreach ( $results as $result ) {
+			if ( '.' == $result || '..' == $result )
+				continue;
+			if ( is_dir( $path . '/' . $result ) ) {
+				if ( ! $depth )
+					continue;
+				$found = self::scandir( $path . '/' . $result, $relative_path . '/' . $result, $extensions, $depth - 1 );
+				$files = array_merge_recursive( $files, $found );
+			} elseif ( preg_match( '~\.(' . $extensions . ')$~', $result, $match ) ) {
+				if ( ! isset( $files[ $match[1] ] ) )
+					$files[ $match[1] ] = array( $relative_path . '/'. $result );
+				else
+					$files[ $match[1] ][] = $relative_path . '/' . $result;
+			}
+		}
+		return $files;
+	}
+
+	/**
+	 * Loads the theme's textdomain.
+	 *
+	 * Translation files are not inherited from the parent theme. Todo: if this fails for the
+	 * child theme, it should probably try to load the parent theme's translations.
+	 *
+	 * @since 3.4.0
+	 * @access public
+	 *
+	 * @return True if the textdomain was successfully loaded or has already been loaded. False if
+	 * 	no textdomain was specified in the file headers, or if the domain could not be loaded.
+	 */
+	public function load_textdomain() {
+		if ( isset( $this->textdomain_loaded ) )
+			return $this->textdomain_loaded;
+
+		$textdomain = $this->get('TextDomain');
+		if ( ! $textdomain ) {
+			$this->textdomain_loaded = false;
+			return false;
+		}
+
+		if ( is_textdomain_loaded( $textdomain ) ) {
+			$this->textdomain_loaded = true;
+			return true;
+		}
+
+		$path = $this->get_stylesheet_directory();
+		if ( $domainpath = $this->get('DomainPath') )
+			$path .= $domainpath;
+
+		$this->textdomain_loaded = load_theme_textdomain( $textdomain, $path );
+		return $this->textdomain_loaded;
+	}
+
+	/**
+	 * Whether the theme is allowed (multisite only).
+	 *
+	 * @since 3.4.0
+	 * @access public
+	 *
+	 * @param string $check Optional. Whether to check only the 'network'-wide settings, the 'site'
+	 * 	settings, or 'both'. Defaults to 'both'.
+	 * @param int $blog_id Optional. Ignored if only network-wide settings are checked. Defaults to current blog.
+	 * @return bool Whether the theme is allowed for the network. Returns true in single-site.
+	 */
+	public function is_allowed( $check = 'both', $blog_id = null ) {
+		if ( ! is_multisite() )
+			return true;
+
+		if ( 'both' == $check || 'network' == $check ) {
+			$allowed = self::get_allowed_on_network();
+			if ( ! empty( $allowed[ $this->get_stylesheet() ] ) )
+				return true;
+		}
+
+		if ( 'both' == $check || 'site' == $check ) {
+			$allowed = self::get_allowed_on_site( $blog_id );
+			if ( ! empty( $allowed[ $this->get_stylesheet() ] ) )
+				return true;
+		}
+
+		return false;
+	}
+
+	/**
+	 * Returns array of stylesheet names of themes allowed on the site or network.
+	 *
+	 * @since 3.4.0
+	 * @access public
+	 *
+	 * @param int $blog_id Optional. Defaults to current blog.
+	 * @return array Array of stylesheet names.
+	 */
+	public static function get_allowed( $blog_id = null ) {
+		return array_merge( self::get_allowed_on_network(), self::get_allowed_on_site( $blog_id ) );
+	}
+
+	/**
+	 * Returns array of stylesheet names of themes allowed on the network.
+	 *
+	 * @since 3.4.0
+	 * @access public
+	 *
+	 * @return array Array of stylesheet names.
+	 */
+	public static function get_allowed_on_network() {
+		static $allowed_themes;
+		if ( ! isset( $allowed_themes ) )
+			$allowed_themes = (array) get_site_option( 'allowedthemes' );
+		return $allowed_themes;
+	}
+
+	/**
+	 * Returns array of stylesheet names of themes allowed on the site.
+	 *
+	 * @since 3.4.0
+	 * @access public
+	 *
+	 * @param int $blog_id Optional. Defaults to current blog.
+	 * @return array Array of stylesheet names.
+	 */
+	public static function get_allowed_on_site( $blog_id = null ) {
+		static $allowed_themes = array();
+		if ( ! $blog_id )
+			$blog_id = get_current_blog_id();
+
+		if ( ! isset( $allowed_themes[ $blog_id ] ) ) {
+			if ( $blog_id == get_current_blog_id() )
+				$allowed_themes[ $blog_id ] = (array) get_option( 'allowedthemes' );
+			else
+				$allowed_themes[ $blog_id ] = (array) get_blog_option( $blog_id, 'allowedthemes' );
+		}
+
+		return $allowed_themes[ $blog_id ];
+	}
+
+	/**
+	 * Sort themes by name.
+	 */
+	public static function sort_by_name( &$themes ) {
+		if ( 0 === strpos( get_locale(), 'en_' ) ) {
+			uasort( $themes, array( 'WP_Theme', '_name_sort' ) );
+		} else {
+			uasort( $themes, array( 'WP_Theme', '_name_sort_i18n' ) );
+		}
+	}
+
+	/**
+	 * Callback function for usort() to naturally sort themes by name.
+	 *
+	 * Accesses the Name header directly from the class for maximum speed.
+	 * Would choke on HTML but we don't care enough to slow it down with strip_tags().
+	 *
+	 * @since 3.4.0
+	 * @access public
+	 */
+	private static function _name_sort( $a, $b ) {
+		return strnatcasecmp( $a->headers['Name'], $b->headers['Name'] );
+	}
+
+	/**
+	 * Name sort (with translation).
+	 *
+	 * @since 3.4.0
+	 * @access public
+	 */
+	private static function _name_sort_i18n( $a, $b ) {
+		// Don't mark up; Do translate.
+		return strnatcasecmp( $a->display( 'Name', false, true ), $b->display( 'Name', false, true ) );
+	}
+}
+
+/**
+ * Returns an array of WP_Theme objects based on the arguments.
+ *
+ * Despite advances over get_themes(), this function is still quite expensive, and grows
+ * linearly with additional themes. Stick to wp_get_theme() if possible.
+ *
+ * @since 3.4.0
+ *
+ * @param array $args Arguments. Currently 'errors' (defaults to false), 'allowed'
+ * 	(true, false; null for either; defaults to null; only applies to multisite), and 'blog_id'
+ * 	(defaults to current blog; used to find allowed themes; only applies to multisite).
+ * @return Array of WP_Theme objects.
+ */
+function wp_get_themes( $args = array() ) {
+	$defaults = array( 'errors' => false, 'allowed' => null, 'blog_id' => 0 );
+	$args = wp_parse_args( $args, $defaults );
+
+	static $_themes;
+	if ( ! isset( $_themes ) ) {
+		$theme_data = search_theme_directories();
+		// Make sure the current theme wins out, in case search_theme_directories() picks the wrong
+		// one in the case of a conflict. (Normally, last registered theme root wins.)
+		$current_theme = get_stylesheet();
+		$current_theme_root = WP_CONTENT_DIR . get_raw_theme_root( $current_theme );
+		foreach ( $theme_data as $theme_slug => $data ) {
+			if ( $current_theme == $theme_slug && $current_theme_root != $data['theme_root'] )
+				$_themes[ $theme_slug ] = new WP_Theme( $theme_slug, $current_theme_root );
+			else
+				$_themes[ $theme_slug ] = new WP_Theme( $theme_slug, $data['theme_root'] );
+		}
+	}
+
+	$themes = $_themes;
+
+	if ( null !== $args['errors'] ) {
+		foreach ( $themes as $theme_slug => $theme ) {
+			if ( $theme->errors() != $args['errors'] )
+				unset( $themes[ $theme_slug ] );
+		}
+	}
+
+	if ( is_multisite() && null !== $args['allowed'] ) {
+		if ( $allowed = $args['allowed'] ) {
+			$allowed = 'network' == $allowed || 'site' == $allowed ? $allowed : 'both';
+			$themes = array_intersect_key( $themes, WP_Theme::get_allowed( $allowed, $args['blog_id'] ) );
+		} else {
+			$themes = array_diff_key( $themes, WP_Theme::get_allowed( 'both', $args['blog_id'] ) );
+		}
+	}
+
+	return $themes;
+}
+
+/**
+ * Gets a WP_Theme object for a theme.
+ *
+ * @since 3.4.0
+ *
+ * @param string $stylesheet Directory name for the theme. Optional. Defaults to current theme.
+ * @param string $theme_root Theme root to look in. Optional. If not specified, get_raw_theme_root()
+ * 	is used for the $stylesheet provided (or current theme).
+ * @return WP_Theme
+ */
+function wp_get_theme( $stylesheet = null, $theme_root = null ) {
+	if ( empty( $stylesheet ) )
+		$stylesheet = get_stylesheet();
+
+	if ( empty( $theme_root ) )
+		$theme_root = WP_CONTENT_DIR . get_raw_theme_root( $stylesheet );
+
+	return new WP_Theme( $stylesheet, $theme_root );
+}
\ No newline at end of file
Index: wp-includes/theme.php
===================================================================
--- wp-includes/theme.php	(revision 19998)
+++ wp-includes/theme.php	(working copy)
@@ -247,224 +247,6 @@
 }
 
 /**
- * Retrieve list of themes with theme data in theme directory.
- *
- * The theme is broken, if it doesn't have a parent theme and is missing either
- * style.css and, or index.php. If the theme has a parent theme then it is
- * broken, if it is missing style.css; index.php is optional. The broken theme
- * list is saved in the {@link $wp_broken_themes} global, which is displayed on
- * the theme list in the administration panels.
- *
- * @since 1.5.0
- * @global array $wp_broken_themes Stores the broken themes.
- * @global array $wp_themes Stores the working themes.
- *
- * @return array Theme list with theme data.
- */
-function get_themes() {
-	global $wp_themes, $wp_broken_themes;
-
-	if ( isset($wp_themes) )
-		return $wp_themes;
-
-	if ( !$theme_files = search_theme_directories() )
-		return false;
-
-	asort( $theme_files );
-
-	$wp_themes = array();
-
-	foreach ( (array) $theme_files as $theme_file ) {
-		$theme_root = $theme_file['theme_root'];
-		$theme_file = $theme_file['theme_file'];
-
-		if ( !is_readable("$theme_root/$theme_file") ) {
-			$wp_broken_themes[$theme_file] = array('Name' => $theme_file, 'Title' => $theme_file, 'Description' => __('File not readable.'));
-			continue;
-		}
-
-		$theme_data = get_theme_data("$theme_root/$theme_file");
-
-		$name        = $theme_data['Name'];
-		$title       = $theme_data['Title'];
-		$description = wptexturize($theme_data['Description']);
-		$version     = $theme_data['Version'];
-		$author      = $theme_data['Author'];
-		$template    = $theme_data['Template'];
-		$stylesheet  = dirname($theme_file);
-
-		$screenshot = false;
-		foreach ( array('png', 'gif', 'jpg', 'jpeg') as $ext ) {
-			if (file_exists("$theme_root/$stylesheet/screenshot.$ext")) {
-				$screenshot = "screenshot.$ext";
-				break;
-			}
-		}
-
-		if ( empty($name) ) {
-			$name = dirname($theme_file);
-			$title = $name;
-		}
-
-		$parent_template = $template;
-
-		if ( empty($template) ) {
-			if ( file_exists("$theme_root/$stylesheet/index.php") )
-				$template = $stylesheet;
-			else
-				continue;
-		}
-
-		$template = trim( $template );
-
-		if ( !file_exists("$theme_root/$template/index.php") ) {
-			$parent_dir = dirname(dirname($theme_file));
-			if ( file_exists("$theme_root/$parent_dir/$template/index.php") ) {
-				$template = "$parent_dir/$template";
-				$template_directory = "$theme_root/$template";
-			} else {
-				/**
-				 * The parent theme doesn't exist in the current theme's folder or sub folder
-				 * so lets use the theme root for the parent template.
-				 */
-				if ( isset($theme_files[$template]) && file_exists( $theme_files[$template]['theme_root'] . "/$template/index.php" ) ) {
-					$template_directory = $theme_files[$template]['theme_root'] . "/$template";
-				} else {
-					if ( empty( $parent_template) )
-						$wp_broken_themes[$name] = array('Name' => $name, 'Title' => $title, 'Description' => __('Template is missing.'), 'error' => 'no_template');
-					else
-						$wp_broken_themes[$name] = array('Name' => $name, 'Title' => $title, 'Description' => sprintf( __('The parent theme is missing. Please install the "%s" parent theme.'),  $parent_template ), 'error' => 'no_parent', 'parent' => $parent_template );
-					continue;
-				}
-
-			}
-		} else {
-			$template_directory = trim( $theme_root . '/' . $template );
-		}
-
-		$stylesheet_files = array();
-		$template_files = array();
-
-		$stylesheet_dir = @ dir("$theme_root/$stylesheet");
-		if ( $stylesheet_dir ) {
-			while ( ($file = $stylesheet_dir->read()) !== false ) {
-				if ( !preg_match('|^\.+$|', $file) ) {
-					if ( preg_match('|\.css$|', $file) )
-						$stylesheet_files[] = "$theme_root/$stylesheet/$file";
-					elseif ( preg_match('|\.php$|', $file) )
-						$template_files[] = "$theme_root/$stylesheet/$file";
-				}
-			}
-			@ $stylesheet_dir->close();
-		}
-
-		$template_dir = @ dir("$template_directory");
-		if ( $template_dir ) {
-			while ( ($file = $template_dir->read()) !== false ) {
-				if ( preg_match('|^\.+$|', $file) )
-					continue;
-				if ( preg_match('|\.php$|', $file) ) {
-					$template_files[] = "$template_directory/$file";
-				} elseif ( is_dir("$template_directory/$file") ) {
-					$template_subdir = @ dir("$template_directory/$file");
-					if ( !$template_subdir )
-						continue;
-					while ( ($subfile = $template_subdir->read()) !== false ) {
-						if ( preg_match('|^\.+$|', $subfile) )
-							continue;
-						if ( preg_match('|\.php$|', $subfile) )
-							$template_files[] = "$template_directory/$file/$subfile";
-					}
-					@ $template_subdir->close();
-				}
-			}
-			@ $template_dir->close();
-		}
-
-		//Make unique and remove duplicates when stylesheet and template are the same i.e. most themes
-		$template_files = array_unique($template_files);
-		$stylesheet_files = array_unique($stylesheet_files);
-
-		$template_dir = $template_directory;
-		$stylesheet_dir = $theme_root . '/' . $stylesheet;
-
-		if ( empty($template_dir) )
-			$template_dir = '/';
-		if ( empty($stylesheet_dir) )
-			$stylesheet_dir = '/';
-
-		// Check for theme name collision. This occurs if a theme is copied to
-		// a new theme directory and the theme header is not updated. Whichever
-		// theme is first keeps the name. Subsequent themes get a suffix applied.
-		// Default themes themes always trump their pretenders.
-		if ( isset($wp_themes[$name]) ) {
-			$trump_cards = array(
-				'classic'      => 'WordPress Classic',
-				'default'      => 'WordPress Default',
-				'twentyten'    => 'Twenty Ten',
-				'twentyeleven' => 'Twenty Eleven',
-				'twentytwelve' => 'Twenty Twelve',
-			);
-			if ( isset( $trump_cards[ $stylesheet ] ) && $name == $trump_cards[ $stylesheet ] ) {
-				// If another theme has claimed to be one of our default themes, move
-				// them aside.
-				$suffix = $wp_themes[$name]['Stylesheet'];
-				$new_name = "$name/$suffix";
-				$wp_themes[$new_name] = $wp_themes[$name];
-				$wp_themes[$new_name]['Name'] = $new_name;
-			} else {
-				$name = "$name/$stylesheet";
-			}
-		}
-
-		$theme_roots[$stylesheet] = str_replace( WP_CONTENT_DIR, '', $theme_root );
-		$wp_themes[$name] = array(
-			'Name' => $name,
-			'Title' => $title,
-			'Description' => $description,
-			'Author' => $author,
-			'Author Name' => $theme_data['AuthorName'],
-			'Author URI' => $theme_data['AuthorURI'],
-			'Version' => $version,
-			'Template' => $template,
-			'Stylesheet' => $stylesheet,
-			'Template Files' => $template_files,
-			'Stylesheet Files' => $stylesheet_files,
-			'Template Dir' => $template_dir,
-			'Stylesheet Dir' => $stylesheet_dir,
-			'Status' => $theme_data['Status'],
-			'Screenshot' => $screenshot,
-			'Tags' => $theme_data['Tags'],
-			'Theme Root' => $theme_root,
-			'Theme Root URI' => str_replace( WP_CONTENT_DIR, content_url(), $theme_root ),
-		);
-	}
-
-	unset($theme_files);
-
-	/* Store theme roots in the DB */
-	if ( get_site_transient( 'theme_roots' ) != $theme_roots )
-		set_site_transient( 'theme_roots', $theme_roots, 7200 ); // cache for two hours
-	unset($theme_roots);
-
-	/* Resolve theme dependencies. */
-	$theme_names = array_keys( $wp_themes );
-	foreach ( (array) $theme_names as $theme_name ) {
-		$wp_themes[$theme_name]['Parent Theme'] = '';
-		if ( $wp_themes[$theme_name]['Stylesheet'] != $wp_themes[$theme_name]['Template'] ) {
-			foreach ( (array) $theme_names as $parent_theme_name ) {
-				if ( ($wp_themes[$parent_theme_name]['Stylesheet'] == $wp_themes[$parent_theme_name]['Template']) && ($wp_themes[$parent_theme_name]['Template'] == $wp_themes[$theme_name]['Template']) ) {
-					$wp_themes[$theme_name]['Parent Theme'] = $wp_themes[$parent_theme_name]['Name'];
-					break;
-				}
-			}
-		}
-	}
-
-	return $wp_themes;
-}
-
-/**
  * Retrieve theme roots.
  *
  * @since 2.9.0
@@ -479,64 +261,22 @@
 
 	$theme_roots = get_site_transient( 'theme_roots' );
 	if ( false === $theme_roots ) {
-		get_themes();
-		$theme_roots = get_site_transient( 'theme_roots' ); // this is set in get_theme()
+		search_theme_directories(); // Regenerated the transient.
+		$theme_roots = get_site_transient( 'theme_roots' );
 	}
 	return $theme_roots;
 }
 
 /**
- * Retrieve theme data.
- *
- * @since 1.5.0
- *
- * @param string $theme Theme name.
- * @return array|null Null, if theme name does not exist. Theme data, if exists.
- */
-function get_theme($theme) {
-	$themes = get_themes();
-
-	if ( is_array( $themes ) && array_key_exists( $theme, $themes ) )
-		return $themes[$theme];
-
-	return null;
-}
-
-/**
  * Retrieve current theme display name.
  *
- * If the 'current_theme' option has already been set, then it will be returned
- * instead. If it is not set, then each theme will be iterated over until both
- * the current stylesheet and current template name.
- *
  * @since 1.5.0
  *
  * @return string
  */
 function get_current_theme() {
-	if ( $theme = get_option('current_theme') )
-		return $theme;
-
-	$themes = get_themes();
-	$current_theme = 'Twenty Eleven';
-
-	if ( $themes ) {
-		$theme_names = array_keys( $themes );
-		$current_template = get_option( 'template' );
-		$current_stylesheet = get_option( 'stylesheet' );
-
-		foreach ( (array) $theme_names as $theme_name ) {
-			if ( $themes[$theme_name]['Stylesheet'] == $current_stylesheet &&
-					$themes[$theme_name]['Template'] == $current_template ) {
-				$current_theme = $themes[$theme_name]['Name'];
-				break;
-			}
-		}
-	}
-
-	update_option('current_theme', $current_theme);
-
-	return $current_theme;
+	$theme = wp_get_theme();
+	return $theme->get('Name');
 }
 
 /**
@@ -574,78 +314,90 @@
  * @return array Valid themes found
  */
 function search_theme_directories() {
-	global $wp_theme_directories, $wp_broken_themes;
+	global $wp_theme_directories;
 	if ( empty( $wp_theme_directories ) )
 		return false;
 
-	$theme_files = array();
-	$wp_broken_themes = array();
+	static $found_themes;
+	if ( isset( $found_themes ) )
+		return $found_themes;
+	$found_themes = array();
 
+	if ( apply_filters( 'wp_cache_themes_persistently', false ) ) {
+		$cached_roots = get_site_transient( 'theme_roots' );
+		if ( is_array( $cached_roots ) ) {
+			foreach ( $cached_roots as $theme_dir => $theme_root ) {
+				$found_themes[ $theme_dir ] = array(
+					'theme_file' => $theme_dir . '/style.css',
+					'theme_root' => $theme_root,
+				);
+			}
+			return $found_themes;
+		}
+	}
+
 	/* Loop the registered theme directories and extract all themes */
 	foreach ( (array) $wp_theme_directories as $theme_root ) {
 		$theme_loc = $theme_root;
 
 		/* We don't want to replace all forward slashes, see Trac #4541 */
 		if ( '/' != WP_CONTENT_DIR )
-			$theme_loc = str_replace(WP_CONTENT_DIR, '', $theme_root);
+			$theme_loc = str_replace( WP_CONTENT_DIR, '', $theme_root );
 
 		/* Files in the root of the current theme directory and one subdir down */
-		$themes_dir = @ opendir($theme_root);
-
-		if ( !$themes_dir )
+		$dirs = @ scandir( $theme_root );
+		if ( ! $dirs )
 			return false;
-
-		while ( ($theme_dir = readdir($themes_dir)) !== false ) {
-			if ( is_dir($theme_root . '/' . $theme_dir) && is_readable($theme_root . '/' . $theme_dir) ) {
-				if ( $theme_dir[0] == '.' || $theme_dir == 'CVS' )
-					continue;
-
-				$stylish_dir = @opendir($theme_root . '/' . $theme_dir);
-				$found_stylesheet = false;
-
-				while ( ($theme_file = readdir($stylish_dir)) !== false ) {
-					if ( $theme_file == 'style.css' ) {
-						$theme_files[$theme_dir] = array( 'theme_file' => $theme_dir . '/' . $theme_file, 'theme_root' => $theme_root );
-						$found_stylesheet = true;
-						break;
-					}
+		foreach ( $dirs as $dir ) {
+			if ( ! is_dir( $theme_root . '/' . $dir ) || $dir[0] == '.' || $dir == 'CVS' )
+				continue;
+			if ( file_exists( $theme_root . '/' . $dir . '/style.css' ) ) {
+				// wp-content/themes/a-single-theme
+				// wp-content/themes is $theme_root, a-single-theme is $dir
+				$found_themes[ $dir ] = array(
+					'theme_file' => $dir . '/style.css',
+					'theme_root' => $theme_root,
+				);
+			} else {
+				$found_theme = false;
+				// wp-content/themes/a-folder-of-themes/*
+				// wp-content/themes is $theme_root, a-folder-of-themes is $dir, then themes are $sub_dirs
+				$sub_dirs = @ scandir( $theme_root . '/' . $dir );
+				if ( ! $sub_dirs )
+					return false;
+				foreach ( $sub_dirs as $sub_dir ) {
+					if ( ! is_dir( $theme_root . '/' . $dir ) || $dir[0] == '.' || $dir == 'CVS' )
+						continue;
+					if ( ! file_exists( $theme_root . '/' . $dir . '/' . $sub_dir . '/style.css' ) )
+						continue;
+					$found_themes[ $dir . '/' . $sub_dir ] = array(
+						'theme_file' => $dir . '/' . $sub_dir . '/style.css',
+						'theme_root' => $theme_root,
+					);
+					$found_theme = true;
 				}
-				@closedir($stylish_dir);
+				// Never mind the above, it's just a theme missing a style.css.
+				// Return it; WP_Theme will catch the error.
+				if ( ! $found_theme )
+					$found_themes[ $dir ] = array(
+						'theme_file' => $dir . '/style.css',
+						'theme_root' => $theme_root,
+					);
+			}
+		}
+	}
 
-				if ( !$found_stylesheet ) { // look for themes in that dir
-					$subdir = "$theme_root/$theme_dir";
-					$subdir_name = $theme_dir;
-					$theme_subdirs = @opendir( $subdir );
+	asort( $found_themes );
 
-					$found_subdir_themes = false;
-					while ( ($theme_subdir = readdir($theme_subdirs)) !== false ) {
-						if ( is_dir( $subdir . '/' . $theme_subdir) && is_readable($subdir . '/' . $theme_subdir) ) {
-							if ( $theme_subdir[0] == '.' || $theme_subdir == 'CVS' )
-								continue;
+	$theme_roots = array();
+	foreach ( $found_themes as $theme_dir => $theme_data ) {
+		$theme_roots[ $theme_dir ] = $theme_data['theme_root'];
+	}
 
-							$stylish_dir = @opendir($subdir . '/' . $theme_subdir);
-							$found_stylesheet = false;
+	if ( $theme_roots != get_site_transient( 'theme_roots' ) )
+		set_site_transient( 'theme_roots', $theme_roots, 7200 ); // cache for two hours
 
-							while ( ($theme_file = readdir($stylish_dir)) !== false ) {
-								if ( $theme_file == 'style.css' ) {
-									$theme_files["$theme_dir/$theme_subdir"] = array( 'theme_file' => $subdir_name . '/' . $theme_subdir . '/' . $theme_file, 'theme_root' => $theme_root );
-									$found_stylesheet = true;
-									$found_subdir_themes = true;
-									break;
-								}
-							}
-							@closedir($stylish_dir);
-						}
-					}
-					@closedir($theme_subdirs);
-					if ( !$found_subdir_themes )
-						$wp_broken_themes[$theme_dir] = array('Name' => $theme_dir, 'Title' => $theme_dir, 'Description' => __('Stylesheet is missing.'));
-				}
-			}
-		}
-		@closedir( $themes_dir );
-	}
-	return $theme_files;
+	return $found_themes;
 }
 
 /**
@@ -725,7 +477,7 @@
 			$theme_root = $theme_roots[$stylesheet_or_template];
 	}
 
-	return $theme_root;
+	return str_replace( WP_CONTENT_DIR, '', $theme_root );
 }
 
 /**
Index: wp-includes/deprecated.php
===================================================================
--- wp-includes/deprecated.php	(revision 19998)
+++ wp-includes/deprecated.php	(working copy)
@@ -2906,4 +2906,50 @@
  */
 function debug_fclose( $fp ) {
 	_deprecated_function( __FUNCTION__, 'error_log()' );
+}
+
+/**
+ * Retrieve list of themes with theme data in theme directory.
+ *
+ * The theme is broken, if it doesn't have a parent theme and is missing either
+ * style.css and, or index.php. If the theme has a parent theme then it is
+ * broken, if it is missing style.css; index.php is optional.
+ *
+ * @since 1.5.0
+ * @global array $wp_themes Stores the working themes.
+ *
+ * @return array Theme list with theme data.
+ */
+function get_themes() {
+	_deprecated_function( __FUNCTION__, '3.4', 'wp_get_themes()' );
+
+	global $wp_themes;
+	if ( isset( $wp_themes ) )
+		return $wp_themes;
+
+	$themes = wp_get_themes();
+	$wp_themes = array();
+
+	foreach ( $themes as $theme ) {
+		$wp_themes[ $theme->get('Name') ] = $theme;
+	}
+
+	return $wp_themes;
+}
+
+/**
+ * Retrieve theme data.
+ *
+ * @since 1.5.0
+ *
+ * @param string $theme Theme name.
+ * @return array|null Null, if theme name does not exist. Theme data, if exists.
+ */
+function get_theme( $theme ) {
+	_deprecated_function( __FUNCTION__, '3.4', 'wp_get_theme($stylesheet)' );
+
+	$themes = get_themes();
+	if ( is_array( $themes ) && array_key_exists( $theme, $themes ) )
+		return $themes[$theme];
+	return null;
 }
\ No newline at end of file
Index: wp-settings.php
===================================================================
--- wp-settings.php	(revision 19998)
+++ wp-settings.php	(working copy)
@@ -112,6 +112,7 @@
 require( ABSPATH . WPINC . '/capabilities.php' );
 require( ABSPATH . WPINC . '/query.php' );
 require( ABSPATH . WPINC . '/theme.php' );
+require( ABSPATH . WPINC . '/class-wp-theme.php' );
 require( ABSPATH . WPINC . '/user.php' );
 require( ABSPATH . WPINC . '/meta.php' );
 require( ABSPATH . WPINC . '/general-template.php' );
Index: wp-admin/includes/ms-deprecated.php
===================================================================
--- wp-admin/includes/ms-deprecated.php	(revision 19998)
+++ wp-admin/includes/ms-deprecated.php	(working copy)
@@ -64,3 +64,13 @@
 	_deprecated_function(__FUNCTION__, '3.0', 'is_network_only_plugin()' );
 	return is_network_only_plugin( $file );
 }
+
+function get_site_allowed_themes() {
+	_deprecated_function( __FUNCTION__, '3.4', 'WP_Theme::get_allowed_on_network()' );
+	return array_map( 'intval', WP_Theme::get_allowed_on_network() );
+}
+
+function wpmu_get_blog_allowedthemes( $blog_id = 0 ) {
+	_deprecated_function( __FUNCTION__, '3.4', 'WP_Theme::get_allowed_on_site()' );
+	return array_map( 'intval', WP_Theme::get_allowed_on_site( $blog_id ) );
+}
\ No newline at end of file
Index: wp-admin/includes/update.php
===================================================================
--- wp-admin/includes/update.php	(revision 19998)
+++ wp-admin/includes/update.php	(working copy)
@@ -217,16 +217,13 @@
 }
 
 function get_theme_updates() {
-	$themes = get_themes();
+	$themes = wp_get_themes();
 	$current = get_site_transient('update_themes');
 	$update_themes = array();
 
-	foreach ( $themes as $theme ) {
-		$theme = (object) $theme;
-		if ( isset($current->response[ $theme->Stylesheet ]) ) {
-			$update_themes[$theme->Stylesheet] = $theme;
-			$update_themes[$theme->Stylesheet]->update = $current->response[ $theme->Stylesheet ];
-		}
+	foreach ( $current->response as $stylesheet => $data ) {
+		$update_themes[ $stylesheet ] = wp_get_theme( $stylesheet );
+		$update_themes[ $stylesheet ]->update = $data;
 	}
 
 	return $update_themes;
Index: wp-admin/includes/upgrade.php
===================================================================
--- wp-admin/includes/upgrade.php	(revision 19998)
+++ wp-admin/includes/upgrade.php	(working copy)
@@ -1154,11 +1154,6 @@
 		$wpdb->query( "DELETE FROM $wpdb->usermeta WHERE meta_key IN ('show_admin_bar_admin', 'plugins_last_view')" );
 	}
 
-	// 3.3-beta. Can remove before release.
-	if ( $wp_current_db_version > 18715 && $wp_current_db_version < 19389
-		&& is_main_site() && ! defined( 'DO_NOT_UPGRADE_GLOBAL_TABLES' ) )
-			delete_metadata( 'user', 0, 'dismissed_wp_pointers', '', true );
-
 	if ( $wp_current_db_version >= 11548 )
 		return;
 
@@ -1235,6 +1230,10 @@
 		$wpdb->query("ALTER TABLE $wpdb->comments DROP INDEX comment_approved");
 		$wpdb->show_errors();
 	}
+
+	if ( $wp_current_db_version < 20000 && is_main_site() && ! defined( 'DO_NOT_UPGRADE_GLOBAL_TABLES' ) ) {
+		$wpdb->query( "DELETE FROM $wpdb->usermeta WHERE meta_key = 'themes_last_view'" );
+	}
 }
 
 /**
Index: wp-admin/includes/class-wp-ms-themes-list-table.php
===================================================================
--- wp-admin/includes/class-wp-ms-themes-list-table.php	(revision 19998)
+++ wp-admin/includes/class-wp-ms-themes-list-table.php	(working copy)
@@ -15,14 +15,9 @@
 	function __construct() {
 		global $status, $page;
 
-		$default_status = get_user_option( 'themes_last_view' );
-		if ( empty( $default_status ) )
-			$default_status = 'all';
-		$status = isset( $_REQUEST['theme_status'] ) ? $_REQUEST['theme_status'] : $default_status;
-		if ( !in_array( $status, array( 'all', 'enabled', 'disabled', 'upgrade', 'search' ) ) )
+		$status = isset( $_REQUEST['theme_status'] ) ? $_REQUEST['theme_status'] : 'all';
+		if ( ! in_array( $status, array( 'all', 'enabled', 'disabled', 'upgrade', 'search' ) ) )
 			$status = 'all';
-		if ( $status != $default_status && 'search' != $status )
-			update_user_meta( get_current_user_id(), 'themes_last_view', $status );
 
 		$page = $this->get_pagenum();
 
@@ -55,53 +50,41 @@
 	}
 
 	function prepare_items() {
-		global $status, $themes, $totals, $page, $orderby, $order, $s;
+		global $status, $totals, $page, $orderby, $order, $s;
 
 		wp_reset_vars( array( 'orderby', 'order', 's' ) );
 
 		$themes = array(
-			'all' => apply_filters( 'all_themes', get_themes() ),
+			'all' => apply_filters( 'all_themes', wp_get_themes() ),
 			'search' => array(),
 			'enabled' => array(),
 			'disabled' => array(),
 			'upgrade' => array()
 		);
 
-		$site_allowed_themes = get_site_allowed_themes();
-		if ( !$this->is_site_themes ) {
-			$allowed_themes = $site_allowed_themes;
-			$themes_per_page = $this->get_items_per_page( 'themes_network_per_page' );
-		} else {
-			$allowed_themes = wpmu_get_blog_allowedthemes( $this->site_id );
+		if ( $this->is_site_themes ) {
 			$themes_per_page = $this->get_items_per_page( 'site_themes_network_per_page' );
+			$allowed_where = 'site';
+		} else {
+			$themes_per_page = $this->get_items_per_page( 'themes_network_per_page' );
+			$allowed_where = 'network';
 		}
 
-		$current = get_site_transient( 'update_themes' );
+		$current = current_user_can( 'update_themes' ) && ! $this->is_site_themes && get_site_transient( 'update_themes' );
 
 		foreach ( (array) $themes['all'] as $key => $theme ) {
-			$theme_key = $theme['Stylesheet'];
-
-			if ( isset( $allowed_themes [ $theme_key ] ) )  {
-				$themes['all'][$key]['enabled'] = true;
-				$themes['enabled'][$key] = $themes['all'][$key];
+			if ( $this->is_site_themes && $theme->is_allowed( 'network' ) ) {
+				unset( $themes['all'][ $key ] );
+				continue;
 			}
-			else {
-				$themes['all'][$key]['enabled'] = false;
-				$themes['disabled'][$key] = $themes['all'][$key];
-			}
-			if ( isset( $current->response[ $theme['Template'] ] ) )
-				$themes['upgrade'][$key] = $themes['all'][$key];
 
-			if ( $this->is_site_themes && isset( $site_allowed_themes[$theme_key] ) ) {
-				unset( $themes['all'][$key] );
-				unset( $themes['enabled'][$key] );
-				unset( $themes['disabled'][$key] );
-			}
+			$filter = $theme->is_allowed( $allowed_where, $this->site_id ) ? 'enabled' : 'disabled';
+			$themes[ $filter ][ $key ] = $themes['all'][ $key ];
+
+			if ( $current && isset( $current->response[ $key ] ) )
+				$themes['upgrade'][ $key ] = $themes['all'][ $key ];
 		}
 
-		if ( !current_user_can( 'update_themes' ) || $this->is_site_themes )
-			$themes['upgrade'] = array();
-
 		if ( $s ) {
 			$status = 'search';
 			$themes['search'] = array_filter( $themes['all'], array( &$this, '_search_callback' ) );
@@ -115,19 +98,27 @@
 			$status = 'all';
 
 		$this->items = $themes[ $status ];
+		WP_Theme::sort_by_name( $this->items );
+
+		$this->has_items = ! empty( $themes['all'] );
 		$total_this_page = $totals[ $status ];
 
 		if ( $orderby ) {
 			$orderby = ucfirst( $orderby );
 			$order = strtoupper( $order );
 
-			uasort( $this->items, array( &$this, '_order_callback' ) );
+			if ( $orderby == 'Name' ) {
+				if ( 'ASC' == $order )
+					$this->items = array_reverse( $this->items );
+			} else {
+				uasort( $this->items, array( &$this, '_order_callback' ) );
+			}
 		}
 
 		$start = ( $page - 1 ) * $themes_per_page;
 
 		if ( $total_this_page > $themes_per_page )
-			$this->items = array_slice( $this->items, $start, $themes_per_page );
+			$this->items = array_slice( $this->items, $start, $themes_per_page, true );
 
 		$this->set_pagination_args( array(
 			'total_items' => $total_this_page,
@@ -140,19 +131,27 @@
 		if ( is_null( $term ) )
 			$term = stripslashes( $_REQUEST['s'] );
 
-		$search_fields = array( 'Name', 'Title', 'Description', 'Author', 'Author Name', 'Author URI', 'Template', 'Stylesheet' );
-		foreach ( $search_fields as $field )
-			if ( stripos( $theme[ $field ], $term ) !== false )
+		foreach ( array( 'Name', 'Description', 'Author', 'Author', 'AuthorURI' ) as $field ) {
+			// Don't mark up; Do translate.
+			if ( false !== stripos( $theme->display( $field, false, true ), $term ) )
 				return true;
+		}
 
+		if ( false !== stripos( $theme->get_stylesheet(), $term ) )
+			return true;
+
+		if ( false !== stripos( $theme->get_template(), $term ) )
+			return true;
+
 		return false;
 	}
 
+	// Not used by any core columns.
 	function _order_callback( $theme_a, $theme_b ) {
 		global $orderby, $order;
 
-		$a = $theme_a[$orderby];
-		$b = $theme_b[$orderby];
+		$a = $theme_a[ $orderby ];
+		$b = $theme_b[ $orderby ];
 
 		if ( $a == $b )
 			return 0;
@@ -164,9 +163,7 @@
 	}
 
 	function no_items() {
-		global $themes;
-
-		if ( !empty( $themes['all'] ) )
+		if ( ! $$this->has_items )
 			_e( 'No themes found.' );
 		else
 			_e( 'You do not appear to have any themes available at this time.' );
@@ -264,10 +261,13 @@
 
 		$context = $status;
 
-		if ( $this->is_site_themes )
+		if ( $this->is_site_themes ) {
 			$url = "site-themes.php?id={$this->site_id}&amp;";
-		else
+			$allowed = $theme->is_allowed( 'site', $this->site_id );
+		} else {
 			$url = 'themes.php?';
+			$allowed = $theme->is_allowed( 'network' );
+		}
 
 		// preorder
 		$actions = array(
@@ -277,30 +277,29 @@
 			'delete' => ''
 		);
 
-		$theme_key = $theme['Stylesheet'];
+		$theme_key = $theme->get_stylesheet();
 
-		if ( empty( $theme['enabled'] ) )
+		if ( ! $allowed )
 			$actions['enable'] = '<a href="' . esc_url( wp_nonce_url($url . 'action=enable&amp;theme=' . $theme_key . '&amp;paged=' . $page . '&amp;s=' . $s, 'enable-theme_' . $theme_key) ) . '" title="' . esc_attr__('Enable this theme') . '" class="edit">' . ( $this->is_site_themes ? __( 'Enable' ) : __( 'Network Enable' ) ) . '</a>';
 		else
 			$actions['disable'] = '<a href="' . esc_url( wp_nonce_url($url . 'action=disable&amp;theme=' . $theme_key . '&amp;paged=' . $page . '&amp;s=' . $s, 'disable-theme_' . $theme_key) ) . '" title="' . esc_attr__('Disable this theme') . '">' . ( $this->is_site_themes ? __( 'Disable' ) : __( 'Network Disable' ) ) . '</a>';
 
 		if ( current_user_can('edit_themes') )
-			$actions['edit'] = '<a href="' . esc_url('theme-editor.php?theme=' . urlencode( $theme['Name'] )) . '" title="' . esc_attr__('Open this theme in the Theme Editor') . '" class="edit">' . __('Edit') . '</a>';
+			$actions['edit'] = '<a href="' . esc_url('theme-editor.php?theme=' .  $theme_key ) . '" title="' . esc_attr__('Open this theme in the Theme Editor') . '" class="edit">' . __('Edit') . '</a>';
 
-		if ( empty( $theme['enabled'] ) && current_user_can( 'delete_themes' ) && ! $this->is_site_themes && $theme_key != get_option( 'stylesheet' ) && $theme_key != get_option( 'template' ) )
+		if ( ! $allowed && current_user_can( 'delete_themes' ) && ! $this->is_site_themes && $theme_key != get_option( 'stylesheet' ) && $theme_key != get_option( 'template' ) )
 			$actions['delete'] = '<a href="' . esc_url( wp_nonce_url( 'themes.php?action=delete-selected&amp;checked[]=' . $theme_key . '&amp;theme_status=' . $context . '&amp;paged=' . $page . '&amp;s=' . $s, 'bulk-themes' ) ) . '" title="' . esc_attr__( 'Delete this theme' ) . '" class="delete">' . __( 'Delete' ) . '</a>';
 
 		$actions = apply_filters( 'theme_action_links', array_filter( $actions ), $theme_key, $theme, $context );
 		$actions = apply_filters( "theme_action_links_$theme_key", $actions, $theme_key, $theme, $context );
 
-		$class = empty( $theme['enabled'] ) ? 'inactive' : 'active';
-		$checkbox_id = "checkbox_" . md5($theme['Name']);
-		$checkbox = "<input type='checkbox' name='checked[]' value='" . esc_attr( $theme_key ) . "' id='" . $checkbox_id . "' /><label class='screen-reader-text' for='" . $checkbox_id . "' >" . __('Select') . " " . $theme['Name'] . "</label>";
+		$class = ! $allowed ? 'inactive' : 'active';
+		$checkbox_id = "checkbox_" . md5( $theme->get('Name') );
+		$checkbox = "<input type='checkbox' name='checked[]' value='" . esc_attr( $theme_key ) . "' id='" . $checkbox_id . "' /><label class='screen-reader-text' for='" . $checkbox_id . "' >" . __('Select') . " " . $theme->display('Name') . "</label>";
 
-		$description = '<p>' . $theme['Description'] . '</p>';
-		$theme_name = $theme['Name'];
+		$description = '<p>' . $theme->display( 'Description' ) . '</p>';
 
-		$id = sanitize_title( $theme_name );
+		$id = sanitize_html_class( $theme->get_stylesheet() );
 
 		echo "<tr id='$id' class='$class'>";
 
@@ -316,25 +315,25 @@
 					echo "<th scope='row' class='check-column'>$checkbox</th>";
 					break;
 				case 'name':
-					echo "<td class='theme-title'$style><strong>$theme_name</strong>";
+					echo "<td class='theme-title'$style><strong>" . $theme->display('Name') . "</strong>";
 					echo $this->row_actions( $actions, true );
 					echo "</td>";
 					break;
 				case 'description':
 					echo "<td class='column-description desc'$style>
-						<div class='theme-description'>$description</div>
+						<div class='theme-description'>" . $theme->display( 'Description' ) . "</div>
 						<div class='$class second theme-version-author-uri'>";
 
 					$theme_meta = array();
 
-					if ( !empty( $theme['Version'] ) )
-						$theme_meta[] = sprintf( __( 'Version %s' ), $theme['Version'] );
+					if ( $theme->get('Version') )
+						$theme_meta[] = sprintf( __( 'Version %s' ), $theme->display('Version') );
 
-					if ( !empty( $theme['Author'] ) )
-						$theme_meta[] = sprintf( __( 'By %s' ), $theme['Author'] );
+					if ( $theme->get('Author') )
+						$theme_meta[] = sprintf( __( 'By %s' ), $theme->display('Author') );
 
-					if ( !empty( $theme['Theme URI'] ) )
-						$theme_meta[] = '<a href="' . $theme['Theme URI'] . '" title="' . esc_attr__( 'Visit theme homepage' ) . '">' . __( 'Visit Theme Site' ) . '</a>';
+					if ( $theme->get('ThemeURI') )
+						$theme_meta[] = '<a href="' . $theme->display('ThemeURI') . '" title="' . esc_attr__( 'Visit theme homepage' ) . '">' . __( 'Visit Theme Site' ) . '</a>';
 
 					$theme_meta = apply_filters( 'theme_row_meta', $theme_meta, $theme_key, $theme, $status );
 					echo implode( ' | ', $theme_meta );
Index: wp-admin/includes/dashboard.php
===================================================================
--- wp-admin/includes/dashboard.php	(revision 19998)
+++ wp-admin/includes/dashboard.php	(working copy)
@@ -383,11 +383,11 @@
 	echo "\n\t</table>\n\t</div>";
 
 	echo "\n\t".'<div class="versions">';
-	$ct = current_theme_info();
+	$theme = wp_get_theme();
 
 	echo "\n\t<p>";
 
-	if ( empty( $ct->stylesheet_dir ) ) {
+	if ( $theme->errors() ) {
 		if ( ! is_multisite() || is_super_admin() )
 			echo '<span class="error-message">' . __('ERROR: The themes directory is either empty or doesn&#8217;t exist. Please check your installation.') . '</span>';
 	} elseif ( ! empty($wp_registered_sidebars) ) {
@@ -401,7 +401,7 @@
 		}
 		$num = number_format_i18n( $num_widgets );
 
-		$switch_themes = $ct->title;
+		$switch_themes = $theme->display('Name');
 		if ( current_user_can( 'switch_themes') )
 			$switch_themes = '<a href="themes.php">' . $switch_themes . '</a>';
 		if ( current_user_can( 'edit_theme_options' ) ) {
@@ -411,9 +411,9 @@
 		}
 	} else {
 		if ( current_user_can( 'switch_themes' ) )
-			printf( __('Theme <span class="b"><a href="themes.php">%1$s</a></span>'), $ct->title );
+			printf( __('Theme <span class="b"><a href="themes.php">%1$s</a></span>'), $theme->display('Name') );
 		else
-			printf( __('Theme <span class="b">%1$s</span>'), $ct->title );
+			printf( __('Theme <span class="b">%1$s</span>'), $theme->display('Name') );
 	}
 	echo '</p>';
 
@@ -1313,14 +1313,14 @@
 	<div class="welcome-panel-column welcome-panel-last">
 		<h4><span class="icon16 icon-appearance"></span> <?php _e( 'Customize Your Site' ); ?></h4>
 		<?php
-		$ct = current_theme_info();
-		if ( empty ( $ct->stylesheet_dir ) ) :
+		$theme = wp_get_theme();
+		if ( $theme->errors() ) :
 			echo '<p>';
 			printf( __( '<a href="%s">Install a theme</a> to get started customizing your site.' ), esc_url( admin_url( 'themes.php' ) ) );
 			echo '</p>';
 		else:
 			$customize_links = array();
-			if ( 'twentyeleven' == $ct->stylesheet )
+			if ( 'twentyeleven' == $theme->get_stylesheet() )
 				$customize_links[] = sprintf( __( '<a href="%s">Choose light or dark</a>' ), esc_url( admin_url( 'themes.php?page=theme_options' ) ) );
 
 			if ( current_theme_supports( 'custom-background' ) )
@@ -1334,7 +1334,7 @@
 
 			if ( ! empty( $customize_links ) ) {
 				echo '<p>';
-				printf( __( 'Use the current theme &mdash; %1$s &mdash; or <a href="%2$s">choose a new one</a>. If you stick with %3$s, here are a few ways to make your site look unique.' ), $ct->title, esc_url( admin_url( 'themes.php' ) ), $ct->title );
+				printf( __( 'Use the current theme &mdash; %1$s &mdash; or <a href="%2$s">choose a new one</a>. If you stick with %1$s, here are a few ways to make your site look unique.' ), $theme->display('Name'), esc_url( admin_url( 'themes.php' ) ) );
 				echo '</p>';
 			?>
 			<ul>
@@ -1345,7 +1345,7 @@
 			<?php
 			} else {
 				echo '<p>';
-				printf( __( 'Use the current theme &mdash; %1$s &mdash; or <a href="%2$s">choose a new one</a>.' ), $ct->title, esc_url( admin_url( 'themes.php' ) ) );
+				printf( __( 'Use the current theme &mdash; %1$s &mdash; or <a href="%2$s">choose a new one</a>.' ), $this->display('Name'), esc_url( admin_url( 'themes.php' ) ) );
 				echo '</p>';
 			}
 		endif; ?>
Index: wp-admin/includes/deprecated.php
===================================================================
--- wp-admin/includes/deprecated.php	(revision 19998)
+++ wp-admin/includes/deprecated.php	(working copy)
@@ -879,4 +879,61 @@
 		$screen = convert_to_screen( $screen );
 
 	WP_Screen::add_old_compat_help( $screen, $help );
+}
+
+/**
+ * Get the allowed themes for the current blog.
+ *
+ * @since 3.0.0
+ * @deprecated 3.4.0
+ * @deprecated Use wp_get_themes()
+ * @see wp_get_themes()
+ *
+ * @return array $themes Array of allowed themes.
+ */
+function get_allowed_themes() {
+	_deprecated_function( __FUNCTION__, '3.4', "wp_get_themes( array( 'allowed' => true ) )" );
+
+	$themes = wp_get_themes( array( 'allowed' => true ) );
+
+	$wp_themes = array();
+	foreach ( $themes as $theme ) {
+		$wp_themes[ $theme->get('Name') ] = $theme;
+	}
+
+	return $wp_themes;
+}
+
+/**
+ * {@internal Missing Short Description}}
+ *
+ * @since 1.5.0
+ *
+ * @return unknown
+ */
+function get_broken_themes() {
+	_deprecated_function( __FUNCTION__, '3.4', "wp_get_themes( array( 'errors' => true )" );
+
+	$themes = wp_get_themes( array( 'errors' => true ) );
+	$broken = array();
+	foreach ( $themes as $theme ) {
+		$broken[ $theme->get('Name') ] = array(
+			'Title' => $theme->get('Name'),
+			'Description' => $theme->errors()->get_error_message(),
+		);
+	}
+	return $broken;
+}
+
+/**
+ * {@internal Missing Short Description}}
+ *
+ * @since 2.0.0
+ *
+ * @return unknown
+ */
+function current_theme_info() {
+	_deprecated_function( __FUNCTION__, '3.4', 'wp_get_theme()' );
+
+	return wp_get_theme();
 }
\ No newline at end of file
Index: wp-admin/includes/schema.php
===================================================================
--- wp-admin/includes/schema.php	(revision 19998)
+++ wp-admin/includes/schema.php	(working copy)
@@ -343,12 +343,9 @@
 
 	$template = WP_DEFAULT_THEME;
 	// If default theme is a child theme, we need to get its template
-	foreach ( (array) get_themes() as $theme ) {
-		if ( WP_DEFAULT_THEME == $theme['Stylesheet'] ) {
-			$template = $theme['Template'];
-			break;
-		}
-	}
+	$theme = wp_get_theme( $template );
+	if ( ! $theme->errors() )
+		$template = $theme->get_template();
 
 	$timezone_string = '';
 	$gmt_offset = 0;
Index: wp-admin/includes/theme.php
===================================================================
--- wp-admin/includes/theme.php	(revision 19998)
+++ wp-admin/includes/theme.php	(working copy)
@@ -7,46 +7,6 @@
  */
 
 /**
- * {@internal Missing Short Description}}
- *
- * @since 2.0.0
- *
- * @return unknown
- */
-function current_theme_info() {
-	$themes = get_themes();
-	$current_theme = get_current_theme();
-
-	if ( ! $themes ) {
-		$ct = new stdClass;
-		$ct->name = $current_theme;
-		return $ct;
-	}
-
-	if ( ! isset( $themes[$current_theme] ) ) {
-		delete_option( 'current_theme' );
-		$current_theme = get_current_theme();
-	}
-
-	$ct = new stdClass;
-	$ct->name = $current_theme;
-	$ct->title = $themes[$current_theme]['Title'];
-	$ct->version = $themes[$current_theme]['Version'];
-	$ct->parent_theme = $themes[$current_theme]['Parent Theme'];
-	$ct->template_dir = $themes[$current_theme]['Template Dir'];
-	$ct->stylesheet_dir = $themes[$current_theme]['Stylesheet Dir'];
-	$ct->template = $themes[$current_theme]['Template'];
-	$ct->stylesheet = $themes[$current_theme]['Stylesheet'];
-	$ct->screenshot = $themes[$current_theme]['Screenshot'];
-	$ct->description = $themes[$current_theme]['Description'];
-	$ct->author = $themes[$current_theme]['Author'];
-	$ct->tags = $themes[$current_theme]['Tags'];
-	$ct->theme_root = $themes[$current_theme]['Theme Root'];
-	$ct->theme_root_uri = $themes[$current_theme]['Theme Root URI'];
-	return $ct;
-}
-
-/**
  * Remove a theme
  *
  * @since 2.8.0
@@ -114,59 +74,6 @@
 }
 
 /**
- * {@internal Missing Short Description}}
- *
- * @since 1.5.0
- *
- * @return unknown
- */
-function get_broken_themes() {
-	global $wp_broken_themes;
-
-	get_themes();
-	return $wp_broken_themes;
-}
-
-/**
- * Get the allowed themes for the current blog.
- *
- * @since 3.0.0
- *
- * @uses get_themes()
- * @uses current_theme_info()
- * @uses get_site_allowed_themes()
- * @uses wpmu_get_blog_allowedthemes
- *
- * @return array $themes Array of allowed themes.
- */
-function get_allowed_themes() {
-	if ( !is_multisite() )
-		return get_themes();
-
-	$themes = get_themes();
-	$ct = current_theme_info();
-	$allowed_themes = apply_filters("allowed_themes", get_site_allowed_themes() );
-	if ( $allowed_themes == false )
-		$allowed_themes = array();
-
-	$blog_allowed_themes = wpmu_get_blog_allowedthemes();
-	if ( is_array( $blog_allowed_themes ) )
-		$allowed_themes = array_merge( $allowed_themes, $blog_allowed_themes );
-
-	if ( isset( $allowed_themes[ esc_html( $ct->stylesheet ) ] ) == false )
-		$allowed_themes[ esc_html( $ct->stylesheet ) ] = true;
-
-	reset( $themes );
-	foreach ( $themes as $key => $theme ) {
-		if ( isset( $allowed_themes[ esc_html( $theme[ 'Stylesheet' ] ) ] ) == false )
-			unset( $themes[ $key ] );
-	}
-	reset( $themes );
-
-	return $themes;
-}
-
-/**
  * Get the Page Templates available in this theme
  *
  * @since 1.5.0
@@ -174,37 +81,7 @@
  * @return array Key is the template name, value is the filename of the template
  */
 function get_page_templates() {
-	$themes = get_themes();
-	$theme = get_current_theme();
-	$templates = $themes[$theme]['Template Files'];
-	$page_templates = array();
-
-	if ( is_array( $templates ) ) {
-		$base = array( trailingslashit(get_template_directory()), trailingslashit(get_stylesheet_directory()) );
-
-		foreach ( $templates as $template ) {
-			$basename = str_replace($base, '', $template);
-
-			// don't allow template files in subdirectories
-			if ( false !== strpos($basename, '/') )
-				continue;
-
-			if ( 'functions.php' == $basename )
-				continue;
-
-			$template_data = implode( '', file( $template ));
-
-			$name = '';
-			if ( preg_match( '|Template Name:(.*)$|mi', $template_data, $name ) )
-				$name = _cleanup_header_comment($name[1]);
-
-			if ( !empty( $name ) ) {
-				$page_templates[trim( $name )] = $basename;
-			}
-		}
-	}
-
-	return $page_templates;
+	return wp_get_theme()->get_page_templates();
 }
 
 /**
@@ -240,16 +117,14 @@
 	if ( !isset($themes_update) )
 		$themes_update = get_site_transient('update_themes');
 
-	if ( is_object($theme) && isset($theme->stylesheet) )
-		$stylesheet = $theme->stylesheet;
-	elseif ( is_array($theme) && isset($theme['Stylesheet']) )
-		$stylesheet = $theme['Stylesheet'];
-	else
-		return false; //No valid info passed.
+	if ( ! is_a( $theme, 'WP_Theme' ) )
+		return;
 
+	$stylesheet = $theme->get_stylesheet();
+
 	if ( isset($themes_update->response[ $stylesheet ]) ) {
 		$update = $themes_update->response[ $stylesheet ];
-		$theme_name = is_object($theme) ? $theme->name : (is_array($theme) ? $theme['Name'] : '');
+		$theme_name = $theme->get('Name');
 		$details_url = add_query_arg(array('TB_iframe' => 'true', 'width' => 1024, 'height' => 800), $update['url']); //Theme browser inside WP? replace this, Also, theme preview JS will override this on the available list.
 		$update_url = wp_nonce_url('update.php?action=upgrade-theme&amp;theme=' . urlencode($stylesheet), 'upgrade-theme_' . $stylesheet);
 		$update_onclick = 'onclick="if ( confirm(\'' . esc_js( __("Updating this theme will lose any customizations you have made. 'Cancel' to stop, 'OK' to update.") ) . '\') ) {return true;}return false;"';
@@ -270,9 +145,10 @@
  *
  * @since 3.1.0
  *
+ * @param bool $api Optional. Whether to hit the API for tags. Defaults to true.
  * @return array Array of features keyed by category with translations keyed by slug.
  */
-function get_theme_feature_list() {
+function get_theme_feature_list( $api = true ) {
 	// Hard-coded list is used if api not accessible.
 	$features = array(
 			__('Colors') => array(
@@ -290,7 +166,7 @@
 				'white'   => __( 'White' ),
 				'yellow'  => __( 'Yellow' ),
 				'dark'    => __( 'Dark' ),
-				'light'   => __( 'Light ')
+				'light'   => __( 'Light' )
 			),
 
 		__('Columns') => array(
@@ -335,7 +211,7 @@
 		)
 	);
 
-	if ( !current_user_can('install_themes') )
+	if ( ! $api || !current_user_can('install_themes') )
 		return $features;
 
 	if ( !$feature_list = get_site_transient( 'wporg_theme_feature_list' ) )
Index: wp-admin/includes/theme-install.php
===================================================================
--- wp-admin/includes/theme-install.php	(revision 19998)
+++ wp-admin/includes/theme-install.php	(working copy)
@@ -265,16 +265,14 @@
 		}
 	}
 
-	$themes = get_themes();
-	foreach ( (array) $themes as $this_theme ) {
-		if ( is_array($this_theme) && $this_theme['Stylesheet'] == $api->slug ) {
-			if ( version_compare( $this_theme['Version'], $api->version, '=' ) ) {
+	$theme = wp_get_theme( $api->slug );
+	if ( is_a( $theme, 'WP_Theme' ) ) {
+		switch ( version_compare( $theme->get('Version'), $api->version ) ) {
+			case 0; // equal
 				$type = 'latest_installed';
-			} elseif ( version_compare( $this_theme['Version'], $api->version, '>' ) ) {
+			case 1: // installed theme > api version
 				$type = 'newer_installed';
-				$newer_version = $this_theme['Version'];
-			}
-			break;
+				$newer_version = $theme->get('Version');
 		}
 	}
 ?>
Index: wp-admin/includes/class-wp-themes-list-table.php
===================================================================
--- wp-admin/includes/class-wp-themes-list-table.php	(revision 19998)
+++ wp-admin/includes/class-wp-themes-list-table.php	(working copy)
@@ -24,12 +24,8 @@
 	}
 
 	function prepare_items() {
-		global $ct;
+		$themes = wp_get_themes( array( 'allowed' => true ) );
 
-		$ct = current_theme_info();
-
-		$themes = get_allowed_themes();
-
 		if ( ! empty( $_REQUEST['s'] ) ) {
 			$search = strtolower( stripslashes( $_REQUEST['s'] ) );
 			$this->search = array_merge( $this->search, array_filter( array_map( 'trim', explode( ',', $search ) ) ) );
@@ -45,13 +41,13 @@
 
 		if ( $this->search || $this->features ) {
 			foreach ( $themes as $key => $theme ) {
-				if ( !$this->search_theme( $theme ) )
+				if ( ! $this->search_theme( $theme ) )
 					unset( $themes[ $key ] );
 			}
 		}
 
-		unset( $themes[$ct->name] );
-		uksort( $themes, "strnatcasecmp" );
+		unset( $themes[ get_option( 'stylesheet' ) ] );
+		WP_Theme::sort_by_name( $themes );
 
 		$per_page = 999;
 		$page = $this->get_pagenum();
@@ -125,103 +121,100 @@
 
 	function display_rows() {
 		$themes = $this->items;
-		$theme_names = array_keys( $themes );
-		natcasesort( $theme_names );
 
-	foreach ( $theme_names as $theme_name ) {
-		$class = array( 'available-theme' );
-	?>
-	<div class="<?php echo join( ' ', $class ); ?>">
-	<?php if ( !empty( $theme_name ) ) :
-	$template = $themes[$theme_name]['Template'];
-	$stylesheet = $themes[$theme_name]['Stylesheet'];
-	$title = $themes[$theme_name]['Title'];
-	$version = $themes[$theme_name]['Version'];
-	$description = $themes[$theme_name]['Description'];
-	$author = $themes[$theme_name]['Author'];
-	$screenshot = $themes[$theme_name]['Screenshot'];
-	$stylesheet_dir = $themes[$theme_name]['Stylesheet Dir'];
-	$template_dir = $themes[$theme_name]['Template Dir'];
-	$parent_theme = $themes[$theme_name]['Parent Theme'];
-	$theme_root = $themes[$theme_name]['Theme Root'];
-	$theme_root_uri = $themes[$theme_name]['Theme Root URI'];
-	$preview_link = esc_url( add_query_arg( array( 'preview' => 1, 'template' => $template, 'stylesheet' => $stylesheet, 'preview_iframe' => true, 'TB_iframe' => 'true' ), home_url( '/' ) ) );
-	$preview_text = esc_attr( sprintf( __( 'Preview of &#8220;%s&#8221;' ), $title ) );
-	$tags = $themes[$theme_name]['Tags'];
-	$thickbox_class = 'thickbox thickbox-preview';
-	$activate_link = wp_nonce_url( "themes.php?action=activate&amp;template=" . urlencode( $template ) . "&amp;stylesheet=" . urlencode( $stylesheet ), 'switch-theme_' . $template );
-	$activate_text = esc_attr( sprintf( __( 'Activate &#8220;%s&#8221;' ), $title ) );
-	$actions = array();
-	$actions[] = '<a href="' . $activate_link . '" class="activatelink" title="' . $activate_text . '">' . __( 'Activate' ) . '</a>';
-	$actions[] = '<a href="' . $preview_link . '" class="thickbox thickbox-preview" title="' . esc_attr( sprintf( __( 'Preview &#8220;%s&#8221;' ), $theme_name ) ) . '">' . __( 'Preview' ) . '</a>';
-	if ( ! is_multisite() && current_user_can( 'delete_themes' ) )
-		$actions[] = '<a class="submitdelete deletion" href="' . wp_nonce_url( "themes.php?action=delete&amp;template=$stylesheet", 'delete-theme_' . $stylesheet ) . '" onclick="' . "return confirm( '" . esc_js( sprintf( __( "You are about to delete this theme '%s'\n  'Cancel' to stop, 'OK' to delete." ), $theme_name ) ) . "' );" . '">' . __( 'Delete' ) . '</a>';
-	$actions = apply_filters( 'theme_action_links', $actions, $themes[$theme_name] );
+		foreach ( $themes as $theme ) {
+			echo '<div class="available-theme">';
 
-	$actions = implode ( ' | ', $actions );
-?>
-		<a href="<?php echo $preview_link; ?>" class="<?php echo $thickbox_class; ?> screenshot">
-<?php if ( $screenshot ) : ?>
-			<img src="<?php echo $theme_root_uri . '/' . $stylesheet . '/' . $screenshot; ?>" alt="" />
-<?php endif; ?>
-		</a>
-<h3><?php
-	/* translators: 1: theme title, 2: theme version, 3: theme author */
-	printf( __( '%1$s %2$s by %3$s' ), $title, $version, $author ) ; ?></h3>
+			$template = $theme->get_template();
+			$stylesheet = $theme->get_stylesheet();
 
-<span class='action-links'><?php echo $actions ?></span>
-<span class="separator hide-if-no-js">| </span><a href="#" class="theme-detail hide-if-no-js" tabindex='4'><?php _e('Details') ?></a>
-<div class="themedetaildiv hide-if-js">
-<p><?php echo $description; ?></p>
-	<?php if ( current_user_can( 'edit_themes' ) && $parent_theme ) {
-	/* translators: 1: theme title, 2:  template dir, 3: stylesheet_dir, 4: theme title, 5: parent_theme */ ?>
-	<p><?php printf( __( 'The template files are located in <code>%2$s</code>. The stylesheet files are located in <code>%3$s</code>. <strong>%4$s</strong> uses templates from <strong>%5$s</strong>. Changes made to the templates will affect both themes.' ), $title, str_replace( WP_CONTENT_DIR, '', $template_dir ), str_replace( WP_CONTENT_DIR, '', $stylesheet_dir ), $title, $parent_theme ); ?></p>
-<?php } else { ?>
-	<p><?php printf( __( 'All of this theme&#8217;s files are located in <code>%2$s</code>.' ), $title, str_replace( WP_CONTENT_DIR, '', $template_dir ), str_replace( WP_CONTENT_DIR, '', $stylesheet_dir ) ); ?></p>
-<?php } ?>
-<?php if ( $tags ) : ?>
-<p><?php _e( 'Tags:' ); ?> <?php echo join( ', ', $tags ); ?></p>
-<?php endif; ?>
-<?php endif; // end if not empty theme_name ?>
-</div>
-	<?php theme_update_available( $themes[$theme_name] ); ?>
-	</div>
-<?php } // end foreach $theme_names
+			$title = $theme->display('Name');
+			$version = $theme->display('Version');
+			$author = $theme->display('Author');
+
+			$activate_link = wp_nonce_url( "themes.php?action=activate&amp;template=" . urlencode( $template ) . "&amp;stylesheet=" . urlencode( $stylesheet ), 'switch-theme_' . $template );
+			$preview_link = esc_url( add_query_arg(
+				array( 'preview' => 1, 'template' => $template, 'stylesheet' => $stylesheet, 'preview_iframe' => true, 'TB_iframe' => 'true' ),
+				home_url( '/' ) ) );
+
+			$actions = array();
+			$actions[] = '<a href="' . $activate_link . '" class="activatelink" title="'
+				. esc_attr( sprintf( __( 'Activate &#8220;%s&#8221;' ), $title ) ) . '">' . __( 'Activate' ) . '</a>';
+			$actions[] = '<a href="' . $preview_link . '" class="thickbox thickbox-preview" title="'
+				. esc_attr( sprintf( __( 'Preview &#8220;%s&#8221;' ), $title ) ) . '">' . __( 'Preview' ) . '</a>';
+			if ( ! is_multisite() && current_user_can( 'delete_themes' ) )
+				$actions[] = '<a class="submitdelete deletion" href="' . wp_nonce_url( "themes.php?action=delete&amp;template=$stylesheet", 'delete-theme_' . $stylesheet )
+					. '" onclick="' . "return confirm( '" . esc_js( sprintf( __( "You are about to delete this theme '%s'\n  'Cancel' to stop, 'OK' to delete." ), $title ) )
+					. "' );" . '">' . __( 'Delete' ) . '</a>';
+
+			$actions = apply_filters( 'theme_action_links', $actions, $theme );
+
+			$actions = implode ( ' | ', $actions );
+			?>
+			<a href="<?php echo $preview_link; ?>" class="thickbox thickbox-preview screenshot">
+			<?php if ( $theme->get_screenshot() ) : ?>
+				<img src="<?php echo esc_url( $theme->get_screenshot( 'absolute' ) ); ?>" alt="" />
+			<?php endif; ?>
+			</a>
+			<h3><?php
+			/* translators: 1: theme title, 2: theme version, 3: theme author */
+			printf( __( '%1$s %2$s by %3$s' ), $title, $version, $author ) ; ?></h3>
+
+			<span class='action-links'><?php echo $actions ?></span>
+			<span class="separator hide-if-no-js">| </span><a href="#" class="theme-detail hide-if-no-js" tabindex='4'><?php _e('Details') ?></a>
+			<div class="themedetaildiv hide-if-js">
+			<p><?php echo $theme->display('Description'); ?></p>
+			<?php if ( current_user_can( 'edit_themes' ) && $theme->parent() ) :
+				/* translators: 1: theme title, 2:  template dir, 3: stylesheet_dir, 4: theme title, 5: parent_theme */ ?>
+				<p><?php printf( __( 'The template files are located in <code>%2$s</code>. The stylesheet files are located in <code>%3$s</code>. <strong>%4$s</strong> uses templates from <strong>%5$s</strong>. Changes made to the templates will affect both themes.' ),
+					$title, str_replace( WP_CONTENT_DIR, '', $theme->get_template_directory() ), str_replace( WP_CONTENT_DIR, '', $theme->get_stylesheet_directory() ), $title, $theme->parent()->display('Name') ); ?></p>
+			<?php else :
+					/* translators: 1: theme title, 2:  template dir, 3: stylesheet_dir */ ?>
+				<p><?php printf( __( 'All of this theme&#8217;s files are located in <code>%2$s</code>.' ),
+					$title, str_replace( WP_CONTENT_DIR, '', $theme->get_template_directory() ), str_replace( WP_CONTENT_DIR, '', $theme->get_stylesheet_directory() ) ); ?></p>
+			<?php endif; ?>
+			<?php
+			if ( $theme->get('Tags') )
+				printf( '<p>' . __( 'Tags: %s.' ) . '</p>', $theme->display('Tags') );
+			?>
+			</div>
+			<?php theme_update_available( $theme ); ?>
+			</div>
+		<?php
+		}
 	}
 
 	function search_theme( $theme ) {
-		$matched = 0;
+		// Search the features
+		if ( $this->features ) {
+			foreach ( $this->features as $word ) {
+				if ( ! in_array( $word, $theme->get('Tags') ) )
+					return false;
+			}
+		}
 
 		// Match all phrases
-		if ( count( $this->search ) > 0 ) {
+		if ( $this->search ) {
 			foreach ( $this->search as $word ) {
-				$matched = 0;
+				if ( in_array( $word, $theme->get('Tags') ) )
+					continue;
 
-				// In a tag?
-				if ( in_array( $word, array_map( 'sanitize_title_with_dashes', $theme['Tags'] ) ) )
-					$matched = 1;
-
-				// In one of the fields?
-				foreach ( array( 'Name', 'Title', 'Description', 'Author', 'Template', 'Stylesheet' ) AS $field ) {
-					if ( stripos( $theme[$field], $word ) !== false )
-						$matched++;
+				foreach ( array( 'Name', 'Description', 'Author', 'AuthorURI' ) as $header ) {
+					// Don't mark up; Do translate.
+					if ( false !== stripos( $theme->display( $header, false, true ), $word ) )
+						continue 2;
 				}
 
-				if ( $matched == 0 )
-					return false;
-			}
-		}
+				if ( false !== stripos( $theme->get_stylesheet(), $word ) )
+					continue;
 
-		// Now search the features
-		if ( count( $this->features ) > 0 ) {
-			foreach ( $this->features as $word ) {
-				// In a tag?
-				if ( !in_array( $word, array_map( 'sanitize_title_with_dashes', $theme['Tags'] ) ) )
-					return false;
+				if ( false !== stripos( $theme->get_template(), $word ) )
+					continue;
+
+				return false;
 			}
 		}
 
-		// Only get here if each word exists in the tags or one of the fields
 		return true;
 	}
 }
Index: wp-admin/includes/ms.php
===================================================================
--- wp-admin/includes/ms.php	(revision 19998)
+++ wp-admin/includes/ms.php	(working copy)
@@ -169,35 +169,6 @@
 	return true;
 }
 
-function wpmu_get_blog_allowedthemes( $blog_id = 0 ) {
-	$themes = get_themes();
-
-	if ( $blog_id != 0 )
-		switch_to_blog( $blog_id );
-
-	$blog_allowed_themes = get_option( 'allowedthemes' );
-	if ( !is_array( $blog_allowed_themes ) || empty( $blog_allowed_themes ) ) { // convert old allowed_themes to new allowedthemes
-		$blog_allowed_themes = get_option( 'allowed_themes' );
-
-		if ( is_array( $blog_allowed_themes ) ) {
-			foreach( (array) $themes as $key => $theme ) {
-				$theme_key = esc_html( $theme['Stylesheet'] );
-				if ( isset( $blog_allowed_themes[$key] ) == true ) {
-					$blog_allowedthemes[$theme_key] = 1;
-				}
-			}
-			$blog_allowed_themes = $blog_allowedthemes;
-			add_option( 'allowedthemes', $blog_allowed_themes );
-			delete_option( 'allowed_themes' );
-		}
-	}
-
-	if ( $blog_id != 0 )
-		restore_current_blog();
-
-	return $blog_allowed_themes;
-}
-
 function update_option_new_admin_email( $old_value, $value ) {
 	$email = get_option( 'admin_email' );
 	if ( $value == get_option( 'admin_email' ) || !is_email( $value ) )
@@ -296,26 +267,6 @@
 }
 add_action( 'admin_notices', 'new_user_email_admin_notice' );
 
-function get_site_allowed_themes() {
-	$themes = get_themes();
-	$allowed_themes = get_site_option( 'allowedthemes' );
-	if ( !is_array( $allowed_themes ) || empty( $allowed_themes ) ) {
-		$allowed_themes = get_site_option( 'allowed_themes' ); // convert old allowed_themes format
-		if ( !is_array( $allowed_themes ) ) {
-			$allowed_themes = array();
-		} else {
-			foreach( (array) $themes as $key => $theme ) {
-				$theme_key = esc_html( $theme['Stylesheet'] );
-				if ( isset( $allowed_themes[ $key ] ) == true ) {
-					$allowedthemes[ $theme_key ] = 1;
-				}
-			}
-			$allowed_themes = $allowedthemes;
-		}
-	}
-	return $allowed_themes;
-}
-
 /**
  * Determines if there is any upload space left in the current blog's quota.
  *
Index: wp-admin/themes.php
===================================================================
--- wp-admin/themes.php	(revision 19998)
+++ wp-admin/themes.php	(working copy)
@@ -96,16 +96,16 @@
 <h2><?php echo esc_html( $title ); ?>
 <?php endif; ?>
 </h2>
-
-<h3><?php _e('Current Theme'); ?></h3>
+<?php $ct = wp_get_theme(); ?>
+<h3><?php _e( 'Current Theme' ); ?></h3>
 <div id="current-theme">
-<?php if ( $ct->screenshot ) : ?>
-<img src="<?php echo $ct->theme_root_uri . '/' . $ct->stylesheet . '/' . $ct->screenshot; ?>" alt="<?php esc_attr_e('Current theme preview'); ?>" />
+<?php if ( $ct->get_screenshot() ) : ?>
+<img src="<?php echo $ct->get_screenshot( 'absolute' ); ?>" alt="<?php esc_attr_e( 'Current theme preview'); ?>" />
 <?php endif; ?>
 <h4><?php
 	/* translators: 1: theme title, 2: theme version, 3: theme author */
-	printf(__('%1$s %2$s by %3$s'), $ct->title, $ct->version, $ct->author) ; ?></h4>
-<p class="theme-description"><?php echo $ct->description; ?></p>
+	printf( __( '%1$s %2$s by %3$s' ), $ct->display('Name'), $ct->display('Version'), $ct->display('Author') ) ; ?></h4>
+<p class="theme-description"><?php echo $ct->display('Description'); ?></p>
 <div class="theme-options">
 	<span><?php _e( 'Options:' )?></span>
 	<?php
@@ -117,8 +117,8 @@
 			if ( 'themes.php' == $item[2] || 'theme-editor.php' == $item[2] )
 				continue;
 			// 0 = name, 1 = capability, 2 = file
-			if ( ( strcmp($self, $item[2]) == 0 && empty($parent_file)) || ($parent_file && ($item[2] == $parent_file)) ) $class = ' class="current"';
-
+			if ( ( strcmp($self, $item[2]) == 0 && empty($parent_file)) || ($parent_file && ($item[2] == $parent_file)) )
+				$class = ' class="current"';
 			if ( !empty($submenu[$item[2]]) ) {
 				$submenu[$item[2]] = array_values($submenu[$item[2]]); // Re-index.
 				$menu_hook = get_plugin_page_hook($submenu[$item[2]][0][2], $item[2]);
@@ -137,8 +137,8 @@
 	}
 	echo implode ( ' | ', $options );
 
-	if ( $ct->tags ) : ?>
-	<p><?php _e('Tags:'); ?> <?php echo join(', ', $ct->tags); ?></p>
+	if ( $ct->get('Tags') ) : ?>
+	<p><?php _e('Tags:'); ?> <?php echo $ct->display('Tags'); ?></p>
 	<?php endif; ?>
 </div>
 <?php theme_update_available($ct); ?>
@@ -218,8 +218,7 @@
 
 <?php
 // List broken themes, if any.
-$broken_themes = get_broken_themes();
-if ( current_user_can('edit_themes') && count( $broken_themes ) ) {
+if ( current_user_can('edit_themes') && $broken_themes = wp_get_themes( array( 'errors' => true ) ) ) {
 ?>
 
 <h3><?php _e('Broken Themes'); ?></h3>
@@ -231,20 +230,13 @@
 		<th><?php _e('Description'); ?></th>
 	</tr>
 <?php
-	$theme = '';
-
-	$theme_names = array_keys($broken_themes);
-	natcasesort($theme_names);
-
-	foreach ($theme_names as $theme_name) {
-		$name = $broken_themes[$theme_name]['Title'];
-		$description = $broken_themes[$theme_name]['Description'];
-
-		$theme = ('class="alternate"' == $theme) ? '' : 'class="alternate"';
+	$alt = '';
+	foreach ( $broken_themes as $broken_theme ) {
+		$alt = ('class="alternate"' == $alt) ? '' : 'class="alternate"';
 		echo "
-		<tr $theme>
-			 <td>$name</td>
-			 <td>$description</td>
+		<tr $alt>
+			 <td>" . $broken_theme->get('Name') ."</td>
+			 <td>" . $broken_theme->errors()->get_error_message() . "</td>
 		</tr>";
 	}
 ?>
@@ -254,4 +246,4 @@
 ?>
 </div>
 
-<?php require('./admin-footer.php'); ?>
+<?php require('./admin-footer.php'); ?>
\ No newline at end of file
