Permalink
Browse files

Introduce WP_Theme, wp_get_themes(), and wp_get_theme() to replace ge…

…t_themes(), get_theme(), get_theme_data(), current_theme_info(), and others.

 * Getters and Helpers: Introduces a series of methods to allow for easy generation of headers for display, and other theme metadata, including page templates.
 * Screenshots: Handles support for multiple screenshots. (see # Additional screenshots must be PNG and start with screenshot-2.png, and be sequential to be counted. see #19816.
 * Error Handling: Broken themes have a WP_Error object attached to them.
 * Caching: Introduces a wp_cache_themes_persistently filter (also in [20020]) to enable persistent caching of all filesystem and sanitization operations normally handled by WP_Theme (and formerly get_file_data() and get_themes()). Themes are cached individually and across five different cache keys for different data pieces.
 * Compatibility: A WP_Theme object is backwards compatible with a theme's array formerly returned by get_themes() and get_theme(), and an stdClass object formerly returned by current_theme_info().
 * i18n/L10n: Theme headers are now localizable with proper Text Domain and Domain Path headers, like plugins. (Language packs may remove the requirement for headers.) For page templates, see #6007 (not fixed yet, but will be easy now). For headers, fixes #15858.
 * PHP and CSS files: New methods that fetch a list of theme files (for the theme editor) only on demand, rather than only loading them into memory. fixes #11214.

Functions deprecated:
 * get_themes(), get_allowed_themes() and get_broken_themes() -- use wp_get_themes()
 * get_theme() and current_theme_info() -- use wp_get_theme()
 * get_site_allowed_themes() -- use WP_Theme::get_allowed_on_network()
 * wpmu_get_blog_allowedthemes() -- use WP_theme::get_allowed_on_site()

see also [20016], [20018], [20019], [20020], [20021], [20022], [20025], [20026], [20027]. also fixes #19244.

see #20103.



git-svn-id: http://svn.automattic.com/wordpress/trunk@20029 1a063a9b-81f0-0310-95a4-ce76da25c4cd
  • Loading branch information...
1 parent f596a23 commit d130a63e253d25dacc91b11146cff51d4a568696 nacin committed Feb 28, 2012
View
120 wp-admin/includes/class-wp-ms-themes-list-table.php
@@ -50,52 +50,40 @@ function ajax_user_can() {
}
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];
- }
- else {
- $themes['all'][$key]['enabled'] = false;
- $themes['disabled'][$key] = $themes['all'][$key];
+ if ( $this->is_site_themes && $theme->is_allowed( 'network' ) ) {
+ unset( $themes['all'][ $key ] );
+ continue;
}
- 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_user_can( 'update_themes' ) || $this->is_site_themes )
- $themes['upgrade'] = array();
+ if ( $current && isset( $current->response[ $key ] ) )
+ $themes['upgrade'][ $key ] = $themes['all'][ $key ];
+ }
if ( $s ) {
$status = 'search';
@@ -110,19 +98,27 @@ function prepare_items() {
$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,
@@ -135,19 +131,27 @@ function _search_callback( $theme ) {
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;
@@ -159,9 +163,7 @@ function _order_callback( $theme_a, $theme_b ) {
}
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.' );
@@ -259,10 +261,13 @@ function single_row( $key, $theme ) {
$context = $status;
- if ( $this->is_site_themes )
+ if ( $this->is_site_themes ) {
$url = "site-themes.php?id={$this->site_id}&";
- else
+ $allowed = $theme->is_allowed( 'site', $this->site_id );
+ } else {
$url = 'themes.php?';
+ $allowed = $theme->is_allowed( 'network' );
+ }
// preorder
$actions = array(
@@ -272,30 +277,29 @@ function single_row( $key, $theme ) {
'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'>";
@@ -311,25 +315,25 @@ function single_row( $key, $theme ) {
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 );
View
158 wp-admin/includes/class-wp-themes-list-table.php
@@ -24,11 +24,7 @@ function ajax_user_can() {
}
function prepare_items() {
- global $ct;
-
- $ct = current_theme_info();
-
- $themes = get_allowed_themes();
+ $themes = wp_get_themes( array( 'allowed' => true ) );
if ( ! empty( $_REQUEST['s'] ) ) {
$search = strtolower( stripslashes( $_REQUEST['s'] ) );
@@ -45,13 +41,13 @@ function prepare_items() {
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,92 +121,98 @@ function get_columns() {
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] );
-
- $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>
-
-<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
+
+ foreach ( $themes as $theme ) {
+ echo '<div class="available-theme">';
+
+ $template = $theme->get_template();
+ $stylesheet = $theme->get_stylesheet();
+
+ $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 ) {
// Search the features
if ( $this->features ) {
foreach ( $this->features as $word ) {
- if ( ! in_array( $word, $theme['Tags'] ) )
+ if ( ! in_array( $word, $theme->get('Tags') ) )
return false;
}
}
// Match all phrases
if ( $this->search ) {
foreach ( $this->search as $word ) {
- if ( in_array( $word, $theme['Tags'] ) )
+ if ( in_array( $word, $theme->get('Tags') ) )
continue;
- }
- foreach ( array( 'Name', 'Title', 'Description', 'Author', 'Template', 'Stylesheet' ) as $header ) {
- if ( false !== stripos( $theme[ $header ], $word ) )
- continue 2;
- }
+ 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 ( false !== stripos( $theme->get_stylesheet(), $word ) )
+ continue;
- return false;
+ if ( false !== stripos( $theme->get_template(), $word ) )
+ continue;
+
+ return false;
+ }
}
return true;
View
20 wp-admin/includes/dashboard.php
@@ -383,11 +383,11 @@ function wp_dashboard_right_now() {
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 @@ function wp_dashboard_right_now() {
}
$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 @@ function wp_dashboard_right_now() {
}
} 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 @@ function wp_welcome_panel() {
<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 @@ function wp_welcome_panel() {
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 @@ function wp_welcome_panel() {
<?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; ?>
View
57 wp-admin/includes/deprecated.php
@@ -879,4 +879,61 @@ function add_contextual_help( $screen, $help ) {
$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();
}
View
10 wp-admin/includes/ms-deprecated.php
@@ -64,3 +64,13 @@ function is_wpmu_sitewide_plugin( $file ) {
_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 ) );
+}
View
49 wp-admin/includes/ms.php
@@ -169,35 +169,6 @@ function wpmu_delete_user( $id ) {
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 @@ function new_user_email_admin_notice() {
}
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.
*
View
9 wp-admin/includes/schema.php
@@ -343,12 +343,9 @@ function populate_options() {
$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;
View
14 wp-admin/includes/theme-install.php
@@ -265,16 +265,14 @@ function install_theme_information() {
}
}
- $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');
}
}
?>
View
137 wp-admin/includes/theme.php
@@ -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,97 +74,14 @@ function delete_theme($template, $redirect = '') {
}
/**
- * {@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
*
* @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 @@ function theme_update_available( $theme ) {
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;"';
View
11 wp-admin/includes/update.php
@@ -217,16 +217,13 @@ function wp_update_plugin($plugin, $feedback = '') {
}
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;
View
44 wp-admin/themes.php
@@ -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'); ?>
View
12 wp-includes/class-wp-customize.php
@@ -242,17 +242,7 @@ public function get_stylesheet_root() {
* @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;
+ return wp_get_theme( $this->stylesheet )->get('Name');
}
/**
View
1,116 wp-includes/class-wp-theme.php
@@ -0,0 +1,1116 @@
+<?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_persistently 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, 'WP_Theme' );
+ if ( self::$persistently_cache ) {
+ wp_cache_add_global_groups( 'themes' );
+ if ( is_int( self::$persistently_cache ) )
+ self::$cache_expiration = self::$persistently_cache;
+ } else {
+ 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' ) as $key ) {
+ if ( isset( $cache[ $key ] ) )
+ $this->$key = $cache[ $key ];
+ }
+ if ( $this->errors )
+ return;
+ if ( isset( $cache['theme_root_template'] ) )
+ $theme_root_template = $cache['theme_root_template'];
+ } 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 template is set from cache, we know it's good.)
+ if ( ! $this->template && ! ( $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 we got our data from cache, we can assume that 'template' is pointing to the right place.
+ if ( ! is_array( $cache ) && $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 ) ) {
+ $cache = array( 'headers' => $this->headers, 'errors' => $this->errors, 'stylesheet' => $this->stylesheet, 'template' => $this->template );
+ // If the parent theme is in another root, we'll want to cache this. Avoids an entire branch of filesystem calls above.
+ if ( isset( $theme_root_template ) )
+ $cache['theme_root_template'] = $theme_root_template;
+ $this->cache_add( 'theme', $cache );
+ }
+ }
+
+ /**
+ * __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 cache_delete() {
+ foreach ( array( 'theme', 'screenshot', 'screenshot_count', 'files', 'headers' ) as $key )
+ wp_cache_delete( $key . '-' . $this->theme_root . '/' . $this->stylesheet, '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;
+ return $this->sanitize_header( $header, $this->headers[ $header ] );
+
+ 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->get_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->get_theme_root_uri();
+ else
+ $theme_root_uri = $this->get_theme_root_uri();
+
+ 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() {
+ if ( 0 === strpos( WP_CONTENT_DIR, $this->theme_root ) )
+ return str_replace( WP_CONTENT_DIR, content_url(), $this->theme_root );
+ // Give up, send it off to the filter.
+ return get_theme_root_uri( $this->stylesheet );
+ }
+
+ /**
+ * 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;
+ }
+
+ /**
+