wp/wp-includes/class-wp-theme.php
changeset 7 cf61fcea0001
parent 5 5e2f62d02dcd
child 9 177826044cd9
equal deleted inserted replaced
6:490d5cc509ed 7:cf61fcea0001
     2 /**
     2 /**
     3  * WP_Theme Class
     3  * WP_Theme Class
     4  *
     4  *
     5  * @package WordPress
     5  * @package WordPress
     6  * @subpackage Theme
     6  * @subpackage Theme
       
     7  * @since 3.4.0
     7  */
     8  */
     8 
       
     9 final class WP_Theme implements ArrayAccess {
     9 final class WP_Theme implements ArrayAccess {
    10 
    10 
    11 	/**
    11 	/**
       
    12 	 * Whether the theme has been marked as updateable.
       
    13 	 *
       
    14 	 * @since 4.4.0
       
    15 	 * @var bool
       
    16 	 *
       
    17 	 * @see WP_MS_Themes_List_Table
       
    18 	 */
       
    19 	public $update = false;
       
    20 
       
    21 	/**
    12 	 * Headers for style.css files.
    22 	 * Headers for style.css files.
    13 	 *
    23 	 *
    14 	 * @static
    24 	 * @static
    15 	 * @access private
       
    16 	 * @var array
    25 	 * @var array
    17 	 */
    26 	 */
    18 	private static $file_headers = array(
    27 	private static $file_headers = array(
    19 		'Name'        => 'Theme Name',
    28 		'Name'        => 'Theme Name',
    20 		'ThemeURI'    => 'Theme URI',
    29 		'ThemeURI'    => 'Theme URI',
    31 
    40 
    32 	/**
    41 	/**
    33 	 * Default themes.
    42 	 * Default themes.
    34 	 *
    43 	 *
    35 	 * @static
    44 	 * @static
    36 	 * @access private
       
    37 	 * @var array
    45 	 * @var array
    38 	 */
    46 	 */
    39 	private static $default_themes = array(
    47 	private static $default_themes = array(
    40 		'classic'        => 'WordPress Classic',
    48 		'classic'         => 'WordPress Classic',
    41 		'default'        => 'WordPress Default',
    49 		'default'         => 'WordPress Default',
    42 		'twentyten'      => 'Twenty Ten',
    50 		'twentyten'       => 'Twenty Ten',
    43 		'twentyeleven'   => 'Twenty Eleven',
    51 		'twentyeleven'    => 'Twenty Eleven',
    44 		'twentytwelve'   => 'Twenty Twelve',
    52 		'twentytwelve'    => 'Twenty Twelve',
    45 		'twentythirteen' => 'Twenty Thirteen',
    53 		'twentythirteen'  => 'Twenty Thirteen',
    46 		'twentyfourteen' => 'Twenty Fourteen',
    54 		'twentyfourteen'  => 'Twenty Fourteen',
    47 		'twentyfifteen'  => 'Twenty Fifteen',
    55 		'twentyfifteen'   => 'Twenty Fifteen',
       
    56 		'twentysixteen'   => 'Twenty Sixteen',
       
    57 		'twentyseventeen' => 'Twenty Seventeen',
    48 	);
    58 	);
    49 
    59 
    50 	/**
    60 	/**
    51 	 * Renamed theme tags.
    61 	 * Renamed theme tags.
       
    62 	 *
       
    63 	 * @static
       
    64 	 * @var array
    52 	 */
    65 	 */
    53 	private static $tag_map = array(
    66 	private static $tag_map = array(
    54 		'fixed-width'    => 'fixed-layout',
    67 		'fixed-width'    => 'fixed-layout',
    55 		'flexible-width' => 'fluid-layout',
    68 		'flexible-width' => 'fluid-layout',
    56 	);
    69 	);
    57 
    70 
    58 	/**
    71 	/**
    59 	 * Absolute path to the theme root, usually wp-content/themes
    72 	 * Absolute path to the theme root, usually wp-content/themes
    60 	 *
    73 	 *
    61 	 * @access private
       
    62 	 * @var string
    74 	 * @var string
    63 	 */
    75 	 */
    64 	private $theme_root;
    76 	private $theme_root;
    65 
    77 
    66 	/**
    78 	/**
    67 	 * Header data from the theme's style.css file.
    79 	 * Header data from the theme's style.css file.
    68 	 *
    80 	 *
    69 	 * @access private
       
    70 	 * @var array
    81 	 * @var array
    71 	 */
    82 	 */
    72 	private $headers = array();
    83 	private $headers = array();
    73 
    84 
    74 	/**
    85 	/**
    75 	 * Header data from the theme's style.css file after being sanitized.
    86 	 * Header data from the theme's style.css file after being sanitized.
    76 	 *
    87 	 *
    77 	 * @access private
       
    78 	 * @var array
    88 	 * @var array
    79 	 */
    89 	 */
    80 	private $headers_sanitized;
    90 	private $headers_sanitized;
    81 
    91 
    82 	/**
    92 	/**
    83 	 * Header name from the theme's style.css after being translated.
    93 	 * Header name from the theme's style.css after being translated.
    84 	 *
    94 	 *
    85 	 * Cached due to sorting functions running over the translated name.
    95 	 * Cached due to sorting functions running over the translated name.
       
    96 	 *
       
    97 	 * @var string
    86 	 */
    98 	 */
    87 	private $name_translated;
    99 	private $name_translated;
    88 
   100 
    89 	/**
   101 	/**
    90 	 * Errors encountered when initializing the theme.
   102 	 * Errors encountered when initializing the theme.
    91 	 *
   103 	 *
    92 	 * @access private
       
    93 	 * @var WP_Error
   104 	 * @var WP_Error
    94 	 */
   105 	 */
    95 	private $errors;
   106 	private $errors;
    96 
   107 
    97 	/**
   108 	/**
    98 	 * The directory name of the theme's files, inside the theme root.
   109 	 * The directory name of the theme's files, inside the theme root.
    99 	 *
   110 	 *
   100 	 * In the case of a child theme, this is directory name of the child theme.
   111 	 * In the case of a child theme, this is directory name of the child theme.
   101 	 * Otherwise, 'stylesheet' is the same as 'template'.
   112 	 * Otherwise, 'stylesheet' is the same as 'template'.
   102 	 *
   113 	 *
   103 	 * @access private
       
   104 	 * @var string
   114 	 * @var string
   105 	 */
   115 	 */
   106 	private $stylesheet;
   116 	private $stylesheet;
   107 
   117 
   108 	/**
   118 	/**
   109 	 * The directory name of the theme's files, inside the theme root.
   119 	 * The directory name of the theme's files, inside the theme root.
   110 	 *
   120 	 *
   111 	 * In the case of a child theme, this is the directory name of the parent theme.
   121 	 * In the case of a child theme, this is the directory name of the parent theme.
   112 	 * Otherwise, 'template' is the same as 'stylesheet'.
   122 	 * Otherwise, 'template' is the same as 'stylesheet'.
   113 	 *
   123 	 *
   114 	 * @access private
       
   115 	 * @var string
   124 	 * @var string
   116 	 */
   125 	 */
   117 	private $template;
   126 	private $template;
   118 
   127 
   119 	/**
   128 	/**
   120 	 * A reference to the parent theme, in the case of a child theme.
   129 	 * A reference to the parent theme, in the case of a child theme.
   121 	 *
   130 	 *
   122 	 * @access private
       
   123 	 * @var WP_Theme
   131 	 * @var WP_Theme
   124 	 */
   132 	 */
   125 	private $parent;
   133 	private $parent;
   126 
   134 
   127 	/**
   135 	/**
   128 	 * URL to the theme root, usually an absolute URL to wp-content/themes
   136 	 * URL to the theme root, usually an absolute URL to wp-content/themes
   129 	 *
   137 	 *
   130 	 * @access private
   138 	 * @var string
   131 	 * var string
       
   132 	 */
   139 	 */
   133 	private $theme_root_uri;
   140 	private $theme_root_uri;
   134 
   141 
   135 	/**
   142 	/**
   136 	 * Flag for whether the theme's textdomain is loaded.
   143 	 * Flag for whether the theme's textdomain is loaded.
   137 	 *
   144 	 *
   138 	 * @access private
       
   139 	 * @var bool
   145 	 * @var bool
   140 	 */
   146 	 */
   141 	private $textdomain_loaded;
   147 	private $textdomain_loaded;
   142 
   148 
   143 	/**
   149 	/**
   144 	 * Stores an md5 hash of the theme root, to function as the cache key.
   150 	 * Stores an md5 hash of the theme root, to function as the cache key.
   145 	 *
   151 	 *
   146 	 * @access private
       
   147 	 * @var string
   152 	 * @var string
   148 	 */
   153 	 */
   149 	private $cache_hash;
   154 	private $cache_hash;
   150 
   155 
   151 	/**
   156 	/**
   152 	 * Flag for whether the themes cache bucket should be persistently cached.
   157 	 * Flag for whether the themes cache bucket should be persistently cached.
   153 	 *
   158 	 *
   154 	 * Default is false. Can be set with the wp_cache_themes_persistently filter.
   159 	 * Default is false. Can be set with the {@see 'wp_cache_themes_persistently'} filter.
   155 	 *
   160 	 *
   156 	 * @access private
   161 	 * @static
   157 	 * @var bool
   162 	 * @var bool
   158 	 */
   163 	 */
   159 	private static $persistently_cache;
   164 	private static $persistently_cache;
   160 
   165 
   161 	/**
   166 	/**
   162 	 * Expiration time for the themes cache bucket.
   167 	 * Expiration time for the themes cache bucket.
   163 	 *
   168 	 *
   164 	 * By default the bucket is not cached, so this value is useless.
   169 	 * By default the bucket is not cached, so this value is useless.
   165 	 *
   170 	 *
   166 	 * @access private
   171 	 * @static
   167 	 * @var bool
   172 	 * @var bool
   168 	 */
   173 	 */
   169 	private static $cache_expiration = 1800;
   174 	private static $cache_expiration = 1800;
   170 
   175 
   171 	/**
   176 	/**
   172 	 * Constructor for WP_Theme.
   177 	 * Constructor for WP_Theme.
       
   178 	 *
       
   179 	 * @since  3.4.0
       
   180 	 *
       
   181 	 * @global array $wp_theme_directories
   173 	 *
   182 	 *
   174 	 * @param string $theme_dir Directory of the theme within the theme_root.
   183 	 * @param string $theme_dir Directory of the theme within the theme_root.
   175 	 * @param string $theme_root Theme root.
   184 	 * @param string $theme_root Theme root.
   176 	 * @param WP_Error|null $_child If this theme is a parent theme, the child may be passed for validation purposes.
   185 	 * @param WP_Error|void $_child If this theme is a parent theme, the child may be passed for validation purposes.
   177 	 */
   186 	 */
   178 	public function __construct( $theme_dir, $theme_root, $_child = null ) {
   187 	public function __construct( $theme_dir, $theme_root, $_child = null ) {
   179 		global $wp_theme_directories;
   188 		global $wp_theme_directories;
   180 
   189 
   181 		// Initialize caching on first run.
   190 		// Initialize caching on first run.
   215 			if ( isset( $cache['theme_root_template'] ) )
   224 			if ( isset( $cache['theme_root_template'] ) )
   216 				$theme_root_template = $cache['theme_root_template'];
   225 				$theme_root_template = $cache['theme_root_template'];
   217 		} elseif ( ! file_exists( $this->theme_root . '/' . $theme_file ) ) {
   226 		} elseif ( ! file_exists( $this->theme_root . '/' . $theme_file ) ) {
   218 			$this->headers['Name'] = $this->stylesheet;
   227 			$this->headers['Name'] = $this->stylesheet;
   219 			if ( ! file_exists( $this->theme_root . '/' . $this->stylesheet ) )
   228 			if ( ! file_exists( $this->theme_root . '/' . $this->stylesheet ) )
   220 				$this->errors = new WP_Error( 'theme_not_found', sprintf( __( 'The theme directory "%s" does not exist.' ), $this->stylesheet ) );
   229 				$this->errors = new WP_Error( 'theme_not_found', sprintf( __( 'The theme directory "%s" does not exist.' ), esc_html( $this->stylesheet ) ) );
   221 			else
   230 			else
   222 				$this->errors = new WP_Error( 'theme_no_stylesheet', __( 'Stylesheet is missing.' ) );
   231 				$this->errors = new WP_Error( 'theme_no_stylesheet', __( 'Stylesheet is missing.' ) );
   223 			$this->template = $this->stylesheet;
   232 			$this->template = $this->stylesheet;
   224 			$this->cache_add( 'theme', array( 'headers' => $this->headers, 'errors' => $this->errors, 'stylesheet' => $this->stylesheet, 'template' => $this->template ) );
   233 			$this->cache_add( 'theme', array( 'headers' => $this->headers, 'errors' => $this->errors, 'stylesheet' => $this->stylesheet, 'template' => $this->template ) );
   225 			if ( ! file_exists( $this->theme_root ) ) // Don't cache this one.
   234 			if ( ! file_exists( $this->theme_root ) ) // Don't cache this one.
   239 				if ( basename( $this->stylesheet ) != $default_theme_slug )
   248 				if ( basename( $this->stylesheet ) != $default_theme_slug )
   240 					$this->headers['Name'] .= '/' . $this->stylesheet;
   249 					$this->headers['Name'] .= '/' . $this->stylesheet;
   241 			}
   250 			}
   242 		}
   251 		}
   243 
   252 
       
   253 		if ( ! $this->template && $this->stylesheet === $this->headers['Template'] ) {
       
   254 			/* translators: %s: Template */
       
   255 			$this->errors = new WP_Error( 'theme_child_invalid', sprintf( __( 'The theme defines itself as its parent theme. Please check the %s header.' ), '<code>Template</code>' ) );
       
   256 			$this->cache_add( 'theme', array( 'headers' => $this->headers, 'errors' => $this->errors, 'stylesheet' => $this->stylesheet ) );
       
   257 
       
   258 			return;
       
   259 		}
       
   260 
   244 		// (If template is set from cache [and there are no errors], we know it's good.)
   261 		// (If template is set from cache [and there are no errors], we know it's good.)
   245 		if ( ! $this->template && ! ( $this->template = $this->headers['Template'] ) ) {
   262 		if ( ! $this->template && ! ( $this->template = $this->headers['Template'] ) ) {
   246 			$this->template = $this->stylesheet;
   263 			$this->template = $this->stylesheet;
   247 			if ( ! file_exists( $this->theme_root . '/' . $this->stylesheet . '/index.php' ) ) {
   264 			if ( ! file_exists( $this->theme_root . '/' . $this->stylesheet . '/index.php' ) ) {
   248 				$this->errors = new WP_Error( 'theme_no_index', __( 'Template is missing.' ) );
   265 				$error_message = sprintf(
       
   266 					/* translators: 1: index.php, 2: Codex URL, 3: style.css */
       
   267 					__( 'Template is missing. Standalone themes need to have a %1$s template file. <a href="%2$s">Child themes</a> need to have a Template header in the %3$s stylesheet.' ),
       
   268 					'<code>index.php</code>',
       
   269 					__( 'https://codex.wordpress.org/Child_Themes' ),
       
   270 					'<code>style.css</code>'
       
   271 				);
       
   272 				$this->errors = new WP_Error( 'theme_no_index', $error_message );
   249 				$this->cache_add( 'theme', array( 'headers' => $this->headers, 'errors' => $this->errors, 'stylesheet' => $this->stylesheet, 'template' => $this->template ) );
   273 				$this->cache_add( 'theme', array( 'headers' => $this->headers, 'errors' => $this->errors, 'stylesheet' => $this->stylesheet, 'template' => $this->template ) );
   250 				return;
   274 				return;
   251 			}
   275 			}
   252 		}
   276 		}
   253 
   277 
   262 				// Look for the template in the search_theme_directories() results, in case it is in another theme root.
   286 				// Look for the template in the search_theme_directories() results, in case it is in another theme root.
   263 				// We don't look into directories of themes, just the theme root.
   287 				// We don't look into directories of themes, just the theme root.
   264 				$theme_root_template = $directories[ $this->template ]['theme_root'];
   288 				$theme_root_template = $directories[ $this->template ]['theme_root'];
   265 			} else {
   289 			} else {
   266 				// Parent theme is missing.
   290 				// Parent theme is missing.
   267 				$this->errors = new WP_Error( 'theme_no_parent', sprintf( __( 'The parent theme is missing. Please install the "%s" parent theme.' ), $this->template ) );
   291 				$this->errors = new WP_Error( 'theme_no_parent', sprintf( __( 'The parent theme is missing. Please install the "%s" parent theme.' ), esc_html( $this->template ) ) );
   268 				$this->cache_add( 'theme', array( 'headers' => $this->headers, 'errors' => $this->errors, 'stylesheet' => $this->stylesheet, 'template' => $this->template ) );
   292 				$this->cache_add( 'theme', array( 'headers' => $this->headers, 'errors' => $this->errors, 'stylesheet' => $this->stylesheet, 'template' => $this->template ) );
   269 				$this->parent = new WP_Theme( $this->template, $this->theme_root, $this );
   293 				$this->parent = new WP_Theme( $this->template, $this->theme_root, $this );
   270 				return;
   294 				return;
   271 			}
   295 			}
   272 		}
   296 		}
   274 		// Set the parent, if we're a child theme.
   298 		// Set the parent, if we're a child theme.
   275 		if ( $this->template != $this->stylesheet ) {
   299 		if ( $this->template != $this->stylesheet ) {
   276 			// If we are a parent, then there is a problem. Only two generations allowed! Cancel things out.
   300 			// If we are a parent, then there is a problem. Only two generations allowed! Cancel things out.
   277 			if ( $_child instanceof WP_Theme && $_child->template == $this->stylesheet ) {
   301 			if ( $_child instanceof WP_Theme && $_child->template == $this->stylesheet ) {
   278 				$_child->parent = null;
   302 				$_child->parent = null;
   279 				$_child->errors = new WP_Error( 'theme_parent_invalid', sprintf( __( 'The "%s" theme is not a valid parent theme.' ), $_child->template ) );
   303 				$_child->errors = new WP_Error( 'theme_parent_invalid', sprintf( __( 'The "%s" theme is not a valid parent theme.' ), esc_html( $_child->template ) ) );
   280 				$_child->cache_add( 'theme', array( 'headers' => $_child->headers, 'errors' => $_child->errors, 'stylesheet' => $_child->stylesheet, 'template' => $_child->template ) );
   304 				$_child->cache_add( 'theme', array( 'headers' => $_child->headers, 'errors' => $_child->errors, 'stylesheet' => $_child->stylesheet, 'template' => $_child->template ) );
   281 				// The two themes actually reference each other with the Template header.
   305 				// The two themes actually reference each other with the Template header.
   282 				if ( $_child->stylesheet == $this->template ) {
   306 				if ( $_child->stylesheet == $this->template ) {
   283 					$this->errors = new WP_Error( 'theme_parent_invalid', sprintf( __( 'The "%s" theme is not a valid parent theme.' ), $this->template ) );
   307 					$this->errors = new WP_Error( 'theme_parent_invalid', sprintf( __( 'The "%s" theme is not a valid parent theme.' ), esc_html( $this->template ) ) );
   284 					$this->cache_add( 'theme', array( 'headers' => $this->headers, 'errors' => $this->errors, 'stylesheet' => $this->stylesheet, 'template' => $this->template ) );
   308 					$this->cache_add( 'theme', array( 'headers' => $this->headers, 'errors' => $this->errors, 'stylesheet' => $this->stylesheet, 'template' => $this->template ) );
   285 				}
   309 				}
   286 				return;
   310 				return;
   287 			}
   311 			}
   288 			// Set the parent. Pass the current instance so we can do the crazy checks above and assess errors.
   312 			// Set the parent. Pass the current instance so we can do the crazy checks above and assess errors.
   300 	}
   324 	}
   301 
   325 
   302 	/**
   326 	/**
   303 	 * When converting the object to a string, the theme name is returned.
   327 	 * When converting the object to a string, the theme name is returned.
   304 	 *
   328 	 *
       
   329 	 * @since  3.4.0
       
   330 	 *
   305 	 * @return string Theme name, ready for display (translated)
   331 	 * @return string Theme name, ready for display (translated)
   306 	 */
   332 	 */
   307 	public function __toString() {
   333 	public function __toString() {
   308 		return (string) $this->display('Name');
   334 		return (string) $this->display('Name');
   309 	}
   335 	}
   310 
   336 
   311 	/**
   337 	/**
   312 	 * __isset() magic method for properties formerly returned by current_theme_info()
   338 	 * __isset() magic method for properties formerly returned by current_theme_info()
       
   339 	 *
       
   340 	 * @staticvar array $properties
       
   341 	 *
       
   342 	 * @since  3.4.0
       
   343 	 *
       
   344 	 * @param string $offset Property to check if set.
       
   345 	 * @return bool Whether the given property is set.
   313 	 */
   346 	 */
   314 	public function __isset( $offset ) {
   347 	public function __isset( $offset ) {
   315 		static $properties = array(
   348 		static $properties = array(
   316 			'name', 'title', 'version', 'parent_theme', 'template_dir', 'stylesheet_dir', 'template', 'stylesheet',
   349 			'name', 'title', 'version', 'parent_theme', 'template_dir', 'stylesheet_dir', 'template', 'stylesheet',
   317 			'screenshot', 'description', 'author', 'tags', 'theme_root', 'theme_root_uri',
   350 			'screenshot', 'description', 'author', 'tags', 'theme_root', 'theme_root_uri',
   320 		return in_array( $offset, $properties );
   353 		return in_array( $offset, $properties );
   321 	}
   354 	}
   322 
   355 
   323 	/**
   356 	/**
   324 	 * __get() magic method for properties formerly returned by current_theme_info()
   357 	 * __get() magic method for properties formerly returned by current_theme_info()
       
   358 	 *
       
   359 	 * @since  3.4.0
       
   360 	 *
       
   361 	 * @param string $offset Property to get.
       
   362 	 * @return mixed Property value.
   325 	 */
   363 	 */
   326 	public function __get( $offset ) {
   364 	public function __get( $offset ) {
   327 		switch ( $offset ) {
   365 		switch ( $offset ) {
   328 			case 'name' :
   366 			case 'name' :
   329 			case 'title' :
   367 			case 'title' :
   359 		}
   397 		}
   360 	}
   398 	}
   361 
   399 
   362 	/**
   400 	/**
   363 	 * Method to implement ArrayAccess for keys formerly returned by get_themes()
   401 	 * Method to implement ArrayAccess for keys formerly returned by get_themes()
       
   402 	 *
       
   403 	 * @since  3.4.0
       
   404 	 *
       
   405 	 * @param mixed $offset
       
   406 	 * @param mixed $value
   364 	 */
   407 	 */
   365 	public function offsetSet( $offset, $value ) {}
   408 	public function offsetSet( $offset, $value ) {}
   366 
   409 
   367 	/**
   410 	/**
   368 	 * Method to implement ArrayAccess for keys formerly returned by get_themes()
   411 	 * Method to implement ArrayAccess for keys formerly returned by get_themes()
       
   412 	 *
       
   413 	 * @since  3.4.0
       
   414 	 *
       
   415 	 * @param mixed $offset
   369 	 */
   416 	 */
   370 	public function offsetUnset( $offset ) {}
   417 	public function offsetUnset( $offset ) {}
   371 
   418 
   372 	/**
   419 	/**
   373 	 * Method to implement ArrayAccess for keys formerly returned by get_themes()
   420 	 * Method to implement ArrayAccess for keys formerly returned by get_themes()
       
   421 	 *
       
   422 	 * @staticvar array $keys
       
   423 	 *
       
   424 	 * @since  3.4.0
       
   425 	 *
       
   426 	 * @param mixed $offset
       
   427 	 * @return bool
   374 	 */
   428 	 */
   375 	public function offsetExists( $offset ) {
   429 	public function offsetExists( $offset ) {
   376 		static $keys = array(
   430 		static $keys = array(
   377 			'Name', 'Version', 'Status', 'Title', 'Author', 'Author Name', 'Author URI', 'Description',
   431 			'Name', 'Version', 'Status', 'Title', 'Author', 'Author Name', 'Author URI', 'Description',
   378 			'Template', 'Stylesheet', 'Template Files', 'Stylesheet Files', 'Template Dir', 'Stylesheet Dir',
   432 			'Template', 'Stylesheet', 'Template Files', 'Stylesheet Files', 'Template Dir', 'Stylesheet Dir',
   379 			 'Screenshot', 'Tags', 'Theme Root', 'Theme Root URI', 'Parent Theme',
   433 			'Screenshot', 'Tags', 'Theme Root', 'Theme Root URI', 'Parent Theme',
   380 		);
   434 		);
   381 
   435 
   382 		return in_array( $offset, $keys );
   436 		return in_array( $offset, $keys );
   383 	}
   437 	}
   384 
   438 
   387 	 *
   441 	 *
   388 	 * Author, Author Name, Author URI, and Description did not previously return
   442 	 * Author, Author Name, Author URI, and Description did not previously return
   389 	 * translated data. We are doing so now as it is safe to do. However, as
   443 	 * translated data. We are doing so now as it is safe to do. However, as
   390 	 * Name and Title could have been used as the key for get_themes(), both remain
   444 	 * Name and Title could have been used as the key for get_themes(), both remain
   391 	 * untranslated for back compatibility. This means that ['Name'] is not ideal,
   445 	 * untranslated for back compatibility. This means that ['Name'] is not ideal,
   392 	 * and care should be taken to use $theme->display('Name') to get a properly
   446 	 * and care should be taken to use `$theme::display( 'Name' )` to get a properly
   393 	 * translated header.
   447 	 * translated header.
       
   448 	 *
       
   449 	 * @since  3.4.0
       
   450 	 *
       
   451 	 * @param mixed $offset
       
   452 	 * @return mixed
   394 	 */
   453 	 */
   395 	public function offsetGet( $offset ) {
   454 	public function offsetGet( $offset ) {
   396 		switch ( $offset ) {
   455 		switch ( $offset ) {
   397 			case 'Name' :
   456 			case 'Name' :
   398 			case 'Title' :
   457 			case 'Title' :
   399 				// See note above about using translated data. get() is not ideal.
   458 				/*
   400 				// It is only for backwards compatibility. Use display().
   459 				 * See note above about using translated data. get() is not ideal.
       
   460 				 * It is only for backward compatibility. Use display().
       
   461 				 */
   401 				return $this->get('Name');
   462 				return $this->get('Name');
   402 			case 'Author' :
   463 			case 'Author' :
   403 				return $this->display( 'Author');
   464 				return $this->display( 'Author');
   404 			case 'Author Name' :
   465 			case 'Author Name' :
   405 				return $this->display( 'Author', false);
   466 				return $this->display( 'Author', false);
   439 
   500 
   440 	/**
   501 	/**
   441 	 * Returns errors property.
   502 	 * Returns errors property.
   442 	 *
   503 	 *
   443 	 * @since 3.4.0
   504 	 * @since 3.4.0
   444 	 * @access public
   505 	 *
   445 	 *
   506 	 * @return WP_Error|false WP_Error if there are errors, or false.
   446 	 * @return WP_Error|bool WP_Error if there are errors, or false.
       
   447 	 */
   507 	 */
   448 	public function errors() {
   508 	public function errors() {
   449 		return is_wp_error( $this->errors ) ? $this->errors : false;
   509 		return is_wp_error( $this->errors ) ? $this->errors : false;
   450 	}
   510 	}
   451 
   511 
   454 	 *
   514 	 *
   455 	 * A theme with errors exists. A theme with the error of 'theme_not_found',
   515 	 * A theme with errors exists. A theme with the error of 'theme_not_found',
   456 	 * meaning that the theme's directory was not found, does not exist.
   516 	 * meaning that the theme's directory was not found, does not exist.
   457 	 *
   517 	 *
   458 	 * @since 3.4.0
   518 	 * @since 3.4.0
   459 	 * @access public
       
   460 	 *
   519 	 *
   461 	 * @return bool Whether the theme exists.
   520 	 * @return bool Whether the theme exists.
   462 	 */
   521 	 */
   463 	public function exists() {
   522 	public function exists() {
   464 		return ! ( $this->errors() && in_array( 'theme_not_found', $this->errors()->get_error_codes() ) );
   523 		return ! ( $this->errors() && in_array( 'theme_not_found', $this->errors()->get_error_codes() ) );
   466 
   525 
   467 	/**
   526 	/**
   468 	 * Returns reference to the parent theme.
   527 	 * Returns reference to the parent theme.
   469 	 *
   528 	 *
   470 	 * @since 3.4.0
   529 	 * @since 3.4.0
   471 	 * @access public
   530 	 *
   472 	 *
   531 	 * @return WP_Theme|false Parent theme, or false if the current theme is not a child theme.
   473 	 * @return WP_Theme|bool Parent theme, or false if the current theme is not a child theme.
       
   474 	 */
   532 	 */
   475 	public function parent() {
   533 	public function parent() {
   476 		return isset( $this->parent ) ? $this->parent : false;
   534 		return isset( $this->parent ) ? $this->parent : false;
   477 	}
   535 	}
   478 
   536 
   479 	/**
   537 	/**
   480 	 * Adds theme data to cache.
   538 	 * Adds theme data to cache.
   481 	 *
   539 	 *
   482 	 * Cache entries keyed by the theme and the type of data.
   540 	 * Cache entries keyed by the theme and the type of data.
   483 	 *
   541 	 *
   484 	 * @access private
   542 	 * @since 3.4.0
   485 	 * @since 3.4.0
   543 	 *
   486 	 *
   544 	 * @param string $key Type of data to store (theme, screenshot, headers, post_templates)
   487 	 * @param string $key Type of data to store (theme, screenshot, headers, page_templates)
       
   488 	 * @param string $data Data to store
   545 	 * @param string $data Data to store
   489 	 * @return bool Return value from wp_cache_add()
   546 	 * @return bool Return value from wp_cache_add()
   490 	 */
   547 	 */
   491 	private function cache_add( $key, $data ) {
   548 	private function cache_add( $key, $data ) {
   492 		return wp_cache_add( $key . '-' . $this->cache_hash, $data, 'themes', self::$cache_expiration );
   549 		return wp_cache_add( $key . '-' . $this->cache_hash, $data, 'themes', self::$cache_expiration );
   495 	/**
   552 	/**
   496 	 * Gets theme data from cache.
   553 	 * Gets theme data from cache.
   497 	 *
   554 	 *
   498 	 * Cache entries are keyed by the theme and the type of data.
   555 	 * Cache entries are keyed by the theme and the type of data.
   499 	 *
   556 	 *
   500 	 * @access private
   557 	 * @since 3.4.0
   501 	 * @since 3.4.0
   558 	 *
   502 	 *
   559 	 * @param string $key Type of data to retrieve (theme, screenshot, headers, post_templates)
   503 	 * @param string $key Type of data to retrieve (theme, screenshot, headers, page_templates)
       
   504 	 * @return mixed Retrieved data
   560 	 * @return mixed Retrieved data
   505 	 */
   561 	 */
   506 	private function cache_get( $key ) {
   562 	private function cache_get( $key ) {
   507 		return wp_cache_get( $key . '-' . $this->cache_hash, 'themes' );
   563 		return wp_cache_get( $key . '-' . $this->cache_hash, 'themes' );
   508 	}
   564 	}
   509 
   565 
   510 	/**
   566 	/**
   511 	 * Clears the cache for the theme.
   567 	 * Clears the cache for the theme.
   512 	 *
   568 	 *
   513 	 * @access public
       
   514 	 * @since 3.4.0
   569 	 * @since 3.4.0
   515 	 */
   570 	 */
   516 	public function cache_delete() {
   571 	public function cache_delete() {
   517 		foreach ( array( 'theme', 'screenshot', 'headers', 'page_templates' ) as $key )
   572 		foreach ( array( 'theme', 'screenshot', 'headers', 'post_templates' ) as $key )
   518 			wp_cache_delete( $key . '-' . $this->cache_hash, 'themes' );
   573 			wp_cache_delete( $key . '-' . $this->cache_hash, 'themes' );
   519 		$this->template = $this->textdomain_loaded = $this->theme_root_uri = $this->parent = $this->errors = $this->headers_sanitized = $this->name_translated = null;
   574 		$this->template = $this->textdomain_loaded = $this->theme_root_uri = $this->parent = $this->errors = $this->headers_sanitized = $this->name_translated = null;
   520 		$this->headers = array();
   575 		$this->headers = array();
   521 		$this->__construct( $this->stylesheet, $this->theme_root );
   576 		$this->__construct( $this->stylesheet, $this->theme_root );
   522 	}
   577 	}
   530 	 * Use the get_template() method, not the 'Template' header, for finding the template.
   585 	 * Use the get_template() method, not the 'Template' header, for finding the template.
   531 	 * The 'Template' header is only good for what was written in the style.css, while
   586 	 * The 'Template' header is only good for what was written in the style.css, while
   532 	 * get_template() takes into account where WordPress actually located the theme and
   587 	 * get_template() takes into account where WordPress actually located the theme and
   533 	 * whether it is actually valid.
   588 	 * whether it is actually valid.
   534 	 *
   589 	 *
   535 	 * @access public
       
   536 	 * @since 3.4.0
   590 	 * @since 3.4.0
   537 	 *
   591 	 *
   538 	 * @param string $header Theme header. Name, Description, Author, Version, ThemeURI, AuthorURI, Status, Tags.
   592 	 * @param string $header Theme header. Name, Description, Author, Version, ThemeURI, AuthorURI, Status, Tags.
   539 	 * @return string|bool String on success, false on failure.
   593 	 * @return string|false String on success, false on failure.
   540 	 */
   594 	 */
   541 	public function get( $header ) {
   595 	public function get( $header ) {
   542 		if ( ! isset( $this->headers[ $header ] ) )
   596 		if ( ! isset( $this->headers[ $header ] ) )
   543 			return false;
   597 			return false;
   544 
   598 
   564 	}
   618 	}
   565 
   619 
   566 	/**
   620 	/**
   567 	 * Gets a theme header, formatted and translated for display.
   621 	 * Gets a theme header, formatted and translated for display.
   568 	 *
   622 	 *
   569 	 * @access public
       
   570 	 * @since 3.4.0
   623 	 * @since 3.4.0
   571 	 *
   624 	 *
   572 	 * @param string $header Theme header. Name, Description, Author, Version, ThemeURI, AuthorURI, Status, Tags.
   625 	 * @param string $header Theme header. Name, Description, Author, Version, ThemeURI, AuthorURI, Status, Tags.
   573 	 * @param bool $markup Optional. Whether to mark up the header. Defaults to true.
   626 	 * @param bool $markup Optional. Whether to mark up the header. Defaults to true.
   574 	 * @param bool $translate Optional. Whether to translate the header. Defaults to true.
   627 	 * @param bool $translate Optional. Whether to translate the header. Defaults to true.
   575 	 * @return string|bool Processed header, false on failure.
   628 	 * @return string|false Processed header, false on failure.
   576 	 */
   629 	 */
   577 	public function display( $header, $markup = true, $translate = true ) {
   630 	public function display( $header, $markup = true, $translate = true ) {
   578 		$value = $this->get( $header );
   631 		$value = $this->get( $header );
   579 		if ( false === $value ) {
   632 		if ( false === $value ) {
   580 			return false;
   633 			return false;
   593 	}
   646 	}
   594 
   647 
   595 	/**
   648 	/**
   596 	 * Sanitize a theme header.
   649 	 * Sanitize a theme header.
   597 	 *
   650 	 *
       
   651 	 * @since 3.4.0
       
   652 	 *
       
   653 	 * @staticvar array $header_tags
       
   654 	 * @staticvar array $header_tags_with_a
       
   655 	 *
   598 	 * @param string $header Theme header. Name, Description, Author, Version, ThemeURI, AuthorURI, Status, Tags.
   656 	 * @param string $header Theme header. Name, Description, Author, Version, ThemeURI, AuthorURI, Status, Tags.
   599 	 * @param string $value Value to sanitize.
   657 	 * @param string $value Value to sanitize.
       
   658 	 * @return mixed
   600 	 */
   659 	 */
   601 	private function sanitize_header( $header, $value ) {
   660 	private function sanitize_header( $header, $value ) {
   602 		switch ( $header ) {
   661 		switch ( $header ) {
   603 			case 'Status' :
   662 			case 'Status' :
   604 				if ( ! $value ) {
   663 				if ( ! $value ) {
   645 	}
   704 	}
   646 
   705 
   647 	/**
   706 	/**
   648 	 * Mark up a theme header.
   707 	 * Mark up a theme header.
   649 	 *
   708 	 *
   650 	 * @access private
   709      * @since 3.4.0
   651 	 * @since 3.4.0
   710 	 *
       
   711 	 * @staticvar string $comma
   652 	 *
   712 	 *
   653 	 * @param string $header Theme header. Name, Description, Author, Version, ThemeURI, AuthorURI, Status, Tags.
   713 	 * @param string $header Theme header. Name, Description, Author, Version, ThemeURI, AuthorURI, Status, Tags.
   654 	 * @param string $value Value to mark up.
   714 	 * @param string $value Value to mark up.
   655 	 * @param string $translate Whether the header has been translated.
   715 	 * @param string $translate Whether the header has been translated.
   656 	 * @return string Value, marked up.
   716 	 * @return string Value, marked up.
   657 	 */
   717 	 */
   658 	private function markup_header( $header, $value, $translate ) {
   718 	private function markup_header( $header, $value, $translate ) {
   659 		switch ( $header ) {
   719 		switch ( $header ) {
   660 			case 'Name' :
   720 			case 'Name' :
   661 				if ( empty( $value ) )
   721 				if ( empty( $value ) ) {
   662 					$value = $this->get_stylesheet();
   722 					$value = esc_html( $this->get_stylesheet() );
       
   723 				}
   663 				break;
   724 				break;
   664 			case 'Description' :
   725 			case 'Description' :
   665 				$value = wptexturize( $value );
   726 				$value = wptexturize( $value );
   666 				break;
   727 				break;
   667 			case 'Author' :
   728 			case 'Author' :
   689 	}
   750 	}
   690 
   751 
   691 	/**
   752 	/**
   692 	 * Translate a theme header.
   753 	 * Translate a theme header.
   693 	 *
   754 	 *
   694 	 * @access private
   755 	 * @since 3.4.0
   695 	 * @since 3.4.0
   756 	 *
       
   757 	 * @staticvar array $tags_list
   696 	 *
   758 	 *
   697 	 * @param string $header Theme header. Name, Description, Author, Version, ThemeURI, AuthorURI, Status, Tags.
   759 	 * @param string $header Theme header. Name, Description, Author, Version, ThemeURI, AuthorURI, Status, Tags.
   698 	 * @param string $value Value to translate.
   760 	 * @param string $value Value to translate.
   699 	 * @return string Translated value.
   761 	 * @return string Translated value.
   700 	 */
   762 	 */
   705 				if ( isset( $this->name_translated ) )
   767 				if ( isset( $this->name_translated ) )
   706 					return $this->name_translated;
   768 					return $this->name_translated;
   707 				$this->name_translated = translate( $value, $this->get('TextDomain' ) );
   769 				$this->name_translated = translate( $value, $this->get('TextDomain' ) );
   708 				return $this->name_translated;
   770 				return $this->name_translated;
   709 			case 'Tags' :
   771 			case 'Tags' :
   710 				if ( empty( $value ) || ! function_exists( 'get_theme_feature_list' ) )
   772 				if ( empty( $value ) || ! function_exists( 'get_theme_feature_list' ) ) {
   711 					return $value;
   773 					return $value;
       
   774 				}
   712 
   775 
   713 				static $tags_list;
   776 				static $tags_list;
   714 				if ( ! isset( $tags_list ) ) {
   777 				if ( ! isset( $tags_list ) ) {
   715 					$tags_list = array();
   778 					$tags_list = array(
       
   779 						// As of 4.6, deprecated tags which are only used to provide translation for older themes.
       
   780 						'black' => __( 'Black' ), 'blue' => __( 'Blue' ), 'brown'  => __( 'Brown' ),
       
   781 						'gray' => __( 'Gray' ), 'green'  => __( 'Green' ), 'orange' => __( 'Orange' ),
       
   782 						'pink' => __( 'Pink' ), 'purple' => __( 'Purple' ), 'red' => __( 'Red' ),
       
   783 						'silver' => __( 'Silver' ), 'tan' => __( 'Tan' ), 'white' => __( 'White' ),
       
   784 						'yellow' => __( 'Yellow' ), 'dark' => __( 'Dark' ), 'light' => __( 'Light' ),
       
   785 						'fixed-layout' => __( 'Fixed Layout' ), 'fluid-layout' => __( 'Fluid Layout' ),
       
   786 						'responsive-layout' => __( 'Responsive Layout' ), 'blavatar' => __( 'Blavatar' ),
       
   787 						'photoblogging' => __( 'Photoblogging' ), 'seasonal' => __( 'Seasonal' ),
       
   788 					);
       
   789 
   716 					$feature_list = get_theme_feature_list( false ); // No API
   790 					$feature_list = get_theme_feature_list( false ); // No API
   717 					foreach ( $feature_list as $tags )
   791 					foreach ( $feature_list as $tags ) {
   718 						$tags_list += $tags;
   792 						$tags_list += $tags;
       
   793 					}
   719 				}
   794 				}
   720 
   795 
   721 				foreach ( $value as &$tag ) {
   796 				foreach ( $value as &$tag ) {
   722 					if ( isset( $tags_list[ $tag ] ) ) {
   797 					if ( isset( $tags_list[ $tag ] ) ) {
   723 						$tag = $tags_list[ $tag ];
   798 						$tag = $tags_list[ $tag ];
   739 	 *
   814 	 *
   740 	 * In the case of a child theme, this is directory name of the child theme.
   815 	 * In the case of a child theme, this is directory name of the child theme.
   741 	 * Otherwise, get_stylesheet() is the same as get_template().
   816 	 * Otherwise, get_stylesheet() is the same as get_template().
   742 	 *
   817 	 *
   743 	 * @since 3.4.0
   818 	 * @since 3.4.0
   744 	 * @access public
       
   745 	 *
   819 	 *
   746 	 * @return string Stylesheet
   820 	 * @return string Stylesheet
   747 	 */
   821 	 */
   748 	public function get_stylesheet() {
   822 	public function get_stylesheet() {
   749 		return $this->stylesheet;
   823 		return $this->stylesheet;
   754 	 *
   828 	 *
   755 	 * In the case of a child theme, this is the directory name of the parent theme.
   829 	 * In the case of a child theme, this is the directory name of the parent theme.
   756 	 * Otherwise, the get_template() is the same as get_stylesheet().
   830 	 * Otherwise, the get_template() is the same as get_stylesheet().
   757 	 *
   831 	 *
   758 	 * @since 3.4.0
   832 	 * @since 3.4.0
   759 	 * @access public
       
   760 	 *
   833 	 *
   761 	 * @return string Template
   834 	 * @return string Template
   762 	 */
   835 	 */
   763 	public function get_template() {
   836 	public function get_template() {
   764 		return $this->template;
   837 		return $this->template;
   769 	 *
   842 	 *
   770 	 * In the case of a child theme, this is the absolute path to the directory
   843 	 * In the case of a child theme, this is the absolute path to the directory
   771 	 * of the child theme's files.
   844 	 * of the child theme's files.
   772 	 *
   845 	 *
   773 	 * @since 3.4.0
   846 	 * @since 3.4.0
   774 	 * @access public
       
   775 	 *
   847 	 *
   776 	 * @return string Absolute path of the stylesheet directory.
   848 	 * @return string Absolute path of the stylesheet directory.
   777 	 */
   849 	 */
   778 	public function get_stylesheet_directory() {
   850 	public function get_stylesheet_directory() {
   779 		if ( $this->errors() && in_array( 'theme_root_missing', $this->errors()->get_error_codes() ) )
   851 		if ( $this->errors() && in_array( 'theme_root_missing', $this->errors()->get_error_codes() ) )
   787 	 *
   859 	 *
   788 	 * In the case of a child theme, this is the absolute path to the directory
   860 	 * In the case of a child theme, this is the absolute path to the directory
   789 	 * of the parent theme's files.
   861 	 * of the parent theme's files.
   790 	 *
   862 	 *
   791 	 * @since 3.4.0
   863 	 * @since 3.4.0
   792 	 * @access public
       
   793 	 *
   864 	 *
   794 	 * @return string Absolute path of the template directory.
   865 	 * @return string Absolute path of the template directory.
   795 	 */
   866 	 */
   796 	public function get_template_directory() {
   867 	public function get_template_directory() {
   797 		if ( $this->parent() )
   868 		if ( $this->parent() )
   807 	 *
   878 	 *
   808 	 * In the case of a child theme, this is the URL to the directory of the
   879 	 * In the case of a child theme, this is the URL to the directory of the
   809 	 * child theme's files.
   880 	 * child theme's files.
   810 	 *
   881 	 *
   811 	 * @since 3.4.0
   882 	 * @since 3.4.0
   812 	 * @access public
       
   813 	 *
   883 	 *
   814 	 * @return string URL to the stylesheet directory.
   884 	 * @return string URL to the stylesheet directory.
   815 	 */
   885 	 */
   816 	public function get_stylesheet_directory_uri() {
   886 	public function get_stylesheet_directory_uri() {
   817 		return $this->get_theme_root_uri() . '/' . str_replace( '%2F', '/', rawurlencode( $this->stylesheet ) );
   887 		return $this->get_theme_root_uri() . '/' . str_replace( '%2F', '/', rawurlencode( $this->stylesheet ) );
   822 	 *
   892 	 *
   823 	 * In the case of a child theme, this is the URL to the directory of the
   893 	 * In the case of a child theme, this is the URL to the directory of the
   824 	 * parent theme's files.
   894 	 * parent theme's files.
   825 	 *
   895 	 *
   826 	 * @since 3.4.0
   896 	 * @since 3.4.0
   827 	 * @access public
       
   828 	 *
   897 	 *
   829 	 * @return string URL to the template directory.
   898 	 * @return string URL to the template directory.
   830 	 */
   899 	 */
   831 	public function get_template_directory_uri() {
   900 	public function get_template_directory_uri() {
   832 		if ( $this->parent() )
   901 		if ( $this->parent() )
   841 	 * The absolute path to the directory of the theme root.
   910 	 * The absolute path to the directory of the theme root.
   842 	 *
   911 	 *
   843 	 * This is typically the absolute path to wp-content/themes.
   912 	 * This is typically the absolute path to wp-content/themes.
   844 	 *
   913 	 *
   845 	 * @since 3.4.0
   914 	 * @since 3.4.0
   846 	 * @access public
       
   847 	 *
   915 	 *
   848 	 * @return string Theme root.
   916 	 * @return string Theme root.
   849 	 */
   917 	 */
   850 	public function get_theme_root() {
   918 	public function get_theme_root() {
   851 		return $this->theme_root;
   919 		return $this->theme_root;
   854 	/**
   922 	/**
   855 	 * Returns the URL to the directory of the theme root.
   923 	 * Returns the URL to the directory of the theme root.
   856 	 *
   924 	 *
   857 	 * This is typically the absolute URL to wp-content/themes. This forms the basis
   925 	 * This is typically the absolute URL to wp-content/themes. This forms the basis
   858 	 * for all other URLs returned by WP_Theme, so we pass it to the public function
   926 	 * for all other URLs returned by WP_Theme, so we pass it to the public function
   859 	 * get_theme_root_uri() and allow it to run the theme_root_uri filter.
   927 	 * get_theme_root_uri() and allow it to run the {@see 'theme_root_uri'} filter.
   860 	 *
   928 	 *
   861 	 * @since 3.4.0
   929 	 * @since 3.4.0
   862 	 * @access public
       
   863 	 *
   930 	 *
   864 	 * @return string Theme root URI.
   931 	 * @return string Theme root URI.
   865 	 */
   932 	 */
   866 	public function get_theme_root_uri() {
   933 	public function get_theme_root_uri() {
   867 		if ( ! isset( $this->theme_root_uri ) )
   934 		if ( ! isset( $this->theme_root_uri ) )
   876 	 *
   943 	 *
   877 	 * Screenshots for a theme must be in the stylesheet directory. (In the case of child
   944 	 * Screenshots for a theme must be in the stylesheet directory. (In the case of child
   878 	 * themes, parent theme screenshots are not inherited.)
   945 	 * themes, parent theme screenshots are not inherited.)
   879 	 *
   946 	 *
   880 	 * @since 3.4.0
   947 	 * @since 3.4.0
   881 	 * @access public
       
   882 	 *
   948 	 *
   883 	 * @param string $uri Type of URL to return, either 'relative' or an absolute URI. Defaults to absolute URI.
   949 	 * @param string $uri Type of URL to return, either 'relative' or an absolute URI. Defaults to absolute URI.
   884 	 * @return mixed Screenshot file. False if the theme does not have a screenshot.
   950 	 * @return string|false Screenshot file. False if the theme does not have a screenshot.
   885 	 */
   951 	 */
   886 	public function get_screenshot( $uri = 'uri' ) {
   952 	public function get_screenshot( $uri = 'uri' ) {
   887 		$screenshot = $this->cache_get( 'screenshot' );
   953 		$screenshot = $this->cache_get( 'screenshot' );
   888 		if ( $screenshot ) {
   954 		if ( $screenshot ) {
   889 			if ( 'relative' == $uri )
   955 			if ( 'relative' == $uri )
   908 
   974 
   909 	/**
   975 	/**
   910 	 * Return files in the theme's directory.
   976 	 * Return files in the theme's directory.
   911 	 *
   977 	 *
   912 	 * @since 3.4.0
   978 	 * @since 3.4.0
   913 	 * @access public
       
   914 	 *
   979 	 *
   915 	 * @param mixed $type Optional. Array of extensions to return. Defaults to all files (null).
   980 	 * @param mixed $type Optional. Array of extensions to return. Defaults to all files (null).
   916 	 * @param int $depth Optional. How deep to search for files. Defaults to a flat scan (0 depth). -1 depth is infinite.
   981 	 * @param int $depth Optional. How deep to search for files. Defaults to a flat scan (0 depth). -1 depth is infinite.
   917 	 * @param bool $search_parent Optional. Whether to return parent files. Defaults to false.
   982 	 * @param bool $search_parent Optional. Whether to return parent files. Defaults to false.
   918 	 * @return array Array of files, keyed by the path to the file relative to the theme's directory, with the values
   983 	 * @return array Array of files, keyed by the path to the file relative to the theme's directory, with the values
   919 	 * 	being absolute paths.
   984 	 *               being absolute paths.
   920 	 */
   985 	 */
   921 	public function get_files( $type = null, $depth = 0, $search_parent = false ) {
   986 	public function get_files( $type = null, $depth = 0, $search_parent = false ) {
   922 		$files = (array) self::scandir( $this->get_stylesheet_directory(), $type, $depth );
   987 		$files = (array) self::scandir( $this->get_stylesheet_directory(), $type, $depth );
   923 
   988 
   924 		if ( $search_parent && $this->parent() )
   989 		if ( $search_parent && $this->parent() ) {
   925 			$files += (array) self::scandir( $this->get_template_directory(), $type, $depth );
   990 			$files += (array) self::scandir( $this->get_template_directory(), $type, $depth );
       
   991 		}
   926 
   992 
   927 		return $files;
   993 		return $files;
   928 	}
   994 	}
   929 
   995 
   930 	/**
   996 	/**
   931 	 * Returns the theme's page templates.
   997 	 * Returns the theme's post templates.
   932 	 *
   998 	 *
   933 	 * @since 3.4.0
   999 	 * @since 4.7.0
   934 	 * @access public
  1000 	 *
   935 	 *
  1001 	 * @return array Array of page templates, keyed by filename and post type,
   936 	 * @param WP_Post|null $post Optional. The post being edited, provided for context.
  1002 	 *               with the value of the translated header name.
       
  1003 	 */
       
  1004 	public function get_post_templates() {
       
  1005 		// If you screw up your current theme and we invalidate your parent, most things still work. Let it slide.
       
  1006 		if ( $this->errors() && $this->errors()->get_error_codes() !== array( 'theme_parent_invalid' ) ) {
       
  1007 			return array();
       
  1008 		}
       
  1009 
       
  1010 		$post_templates = $this->cache_get( 'post_templates' );
       
  1011 
       
  1012 		if ( ! is_array( $post_templates ) ) {
       
  1013 			$post_templates = array();
       
  1014 
       
  1015 			$files = (array) $this->get_files( 'php', 1, true);
       
  1016 
       
  1017 			foreach ( $files as $file => $full_path ) {
       
  1018 				if ( ! preg_match( '|Template Name:(.*)$|mi', file_get_contents( $full_path ), $header ) ) {
       
  1019 					continue;
       
  1020 				}
       
  1021 
       
  1022 				$types = array( 'page' );
       
  1023 				if ( preg_match( '|Template Post Type:(.*)$|mi', file_get_contents( $full_path ), $type ) ) {
       
  1024 					$types = explode( ',', _cleanup_header_comment( $type[1] ) );
       
  1025 				}
       
  1026 
       
  1027 				foreach ( $types as $type ) {
       
  1028 					$type = sanitize_key( $type );
       
  1029 					if ( ! isset( $post_templates[ $type ] ) ) {
       
  1030 						$post_templates[ $type ] = array();
       
  1031 					}
       
  1032 
       
  1033 					$post_templates[ $type ][ $file ] = _cleanup_header_comment( $header[1] );
       
  1034 				}
       
  1035 			}
       
  1036 
       
  1037 			$this->cache_add( 'post_templates', $post_templates );
       
  1038 		}
       
  1039 
       
  1040 		if ( $this->load_textdomain() ) {
       
  1041 			foreach ( $post_templates as &$post_type ) {
       
  1042 				foreach ( $post_type as &$post_template ) {
       
  1043 					$post_template = $this->translate_header( 'Template Name', $post_template );
       
  1044 				}
       
  1045 			}
       
  1046 		}
       
  1047 
       
  1048 		return $post_templates;
       
  1049 	}
       
  1050 
       
  1051 	/**
       
  1052 	 * Returns the theme's post templates for a given post type.
       
  1053 	 *
       
  1054 	 * @since 3.4.0
       
  1055 	 * @since 4.7.0 Added the `$post_type` parameter.
       
  1056 	 *
       
  1057 	 * @param WP_Post|null $post      Optional. The post being edited, provided for context.
       
  1058 	 * @param string       $post_type Optional. Post type to get the templates for. Default 'page'.
       
  1059 	 *                                If a post is provided, its post type is used.
   937 	 * @return array Array of page templates, keyed by filename, with the value of the translated header name.
  1060 	 * @return array Array of page templates, keyed by filename, with the value of the translated header name.
   938 	 */
  1061 	 */
   939 	public function get_page_templates( $post = null ) {
  1062 	public function get_page_templates( $post = null, $post_type = 'page' ) {
   940 		// If you screw up your current theme and we invalidate your parent, most things still work. Let it slide.
  1063 		if ( $post ) {
   941 		if ( $this->errors() && $this->errors()->get_error_codes() !== array( 'theme_parent_invalid' ) )
  1064 			$post_type = get_post_type( $post );
   942 			return array();
  1065 		}
   943 
  1066 
   944 		$page_templates = $this->cache_get( 'page_templates' );
  1067 		$post_templates = $this->get_post_templates();
   945 
  1068 		$post_templates = isset( $post_templates[ $post_type ] ) ? $post_templates[ $post_type ] : array();
   946 		if ( ! is_array( $page_templates ) ) {
       
   947 			$page_templates = array();
       
   948 
       
   949 			$files = (array) $this->get_files( 'php', 1 );
       
   950 
       
   951 			foreach ( $files as $file => $full_path ) {
       
   952 				if ( ! preg_match( '|Template Name:(.*)$|mi', file_get_contents( $full_path ), $header ) )
       
   953 					continue;
       
   954 				$page_templates[ $file ] = _cleanup_header_comment( $header[1] );
       
   955 			}
       
   956 
       
   957 			$this->cache_add( 'page_templates', $page_templates );
       
   958 		}
       
   959 
       
   960 		if ( $this->load_textdomain() ) {
       
   961 			foreach ( $page_templates as &$page_template ) {
       
   962 				$page_template = $this->translate_header( 'Template Name', $page_template );
       
   963 			}
       
   964 		}
       
   965 
       
   966 		if ( $this->parent() )
       
   967 			$page_templates += $this->parent()->get_page_templates( $post );
       
   968 
  1069 
   969 		/**
  1070 		/**
   970 		 * Filter list of page templates for a theme.
  1071 		 * Filters list of page templates for a theme.
   971 		 *
  1072 		 *
   972 		 * This filter does not currently allow for page templates to be added.
  1073 		 * @since 4.9.6
   973 		 *
  1074 		 *
   974 		 * @since 3.9.0
  1075 		 * @param string[]     $post_templates Array of page templates. Keys are filenames,
   975 		 *
       
   976 		 * @param array        $page_templates Array of page templates. Keys are filenames,
       
   977 		 *                                     values are translated names.
  1076 		 *                                     values are translated names.
   978 		 * @param WP_Theme     $this           The theme object.
  1077 		 * @param WP_Theme     $this           The theme object.
   979 		 * @param WP_Post|null $post           The post being edited, provided for context, or null.
  1078 		 * @param WP_Post|null $post           The post being edited, provided for context, or null.
       
  1079 		 * @param string       $post_type      Post type to get the templates for.
   980 		 */
  1080 		 */
   981 		$return = apply_filters( 'theme_page_templates', $page_templates, $this, $post );
  1081 		$post_templates = (array) apply_filters( 'theme_templates', $post_templates, $this, $post, $post_type );
   982 
  1082 
   983 		return array_intersect_assoc( $return, $page_templates );
  1083 		/**
       
  1084 		 * Filters list of page templates for a theme.
       
  1085 		 *
       
  1086 		 * The dynamic portion of the hook name, `$post_type`, refers to the post type.
       
  1087 		 *
       
  1088 		 * @since 3.9.0
       
  1089 		 * @since 4.4.0 Converted to allow complete control over the `$page_templates` array.
       
  1090 		 * @since 4.7.0 Added the `$post_type` parameter.
       
  1091 		 *
       
  1092 		 * @param array        $post_templates Array of page templates. Keys are filenames,
       
  1093 		 *                                     values are translated names.
       
  1094 		 * @param WP_Theme     $this           The theme object.
       
  1095 		 * @param WP_Post|null $post           The post being edited, provided for context, or null.
       
  1096 		 * @param string       $post_type      Post type to get the templates for.
       
  1097 		 */
       
  1098 		$post_templates = (array) apply_filters( "theme_{$post_type}_templates", $post_templates, $this, $post, $post_type );
       
  1099 
       
  1100 		return $post_templates;
   984 	}
  1101 	}
   985 
  1102 
   986 	/**
  1103 	/**
   987 	 * Scans a directory for files of a certain extension.
  1104 	 * Scans a directory for files of a certain extension.
   988 	 *
  1105 	 *
   989 	 * @since 3.4.0
  1106 	 * @since 3.4.0
   990 	 * @access private
  1107 	 *
   991 	 *
  1108 	 * @static
   992 	 * @param string $path Absolute path to search.
  1109 	 *
   993 	 * @param mixed  Array of extensions to find, string of a single extension, or null for all extensions.
  1110 	 * @param string            $path          Absolute path to search.
   994 	 * @param int $depth How deep to search for files. Optional, defaults to a flat scan (0 depth). -1 depth is infinite.
  1111 	 * @param array|string|null $extensions    Optional. Array of extensions to find, string of a single extension,
   995 	 * @param string $relative_path The basename of the absolute path. Used to control the returned path
  1112 	 *                                         or null for all extensions. Default null.
   996 	 * 	for the found files, particularly when this function recurses to lower depths.
  1113 	 * @param int               $depth         Optional. How many levels deep to search for files. Accepts 0, 1+, or
       
  1114 	 *                                         -1 (infinite depth). Default 0.
       
  1115 	 * @param string            $relative_path Optional. The basename of the absolute path. Used to control the
       
  1116 	 *                                         returned path for the found files, particularly when this function
       
  1117 	 *                                         recurses to lower depths. Default empty.
       
  1118 	 * @return array|false Array of files, keyed by the path to the file relative to the `$path` directory prepended
       
  1119 	 *                     with `$relative_path`, with the values being absolute paths. False otherwise.
   997 	 */
  1120 	 */
   998 	private static function scandir( $path, $extensions = null, $depth = 0, $relative_path = '' ) {
  1121 	private static function scandir( $path, $extensions = null, $depth = 0, $relative_path = '' ) {
   999 		if ( ! is_dir( $path ) )
  1122 		if ( ! is_dir( $path ) ) {
  1000 			return false;
  1123 			return false;
       
  1124 		}
  1001 
  1125 
  1002 		if ( $extensions ) {
  1126 		if ( $extensions ) {
  1003 			$extensions = (array) $extensions;
  1127 			$extensions = (array) $extensions;
  1004 			$_extensions = implode( '|', $extensions );
  1128 			$_extensions = implode( '|', $extensions );
  1005 		}
  1129 		}
  1006 
  1130 
  1007 		$relative_path = trailingslashit( $relative_path );
  1131 		$relative_path = trailingslashit( $relative_path );
  1008 		if ( '/' == $relative_path )
  1132 		if ( '/' == $relative_path ) {
  1009 			$relative_path = '';
  1133 			$relative_path = '';
       
  1134 		}
  1010 
  1135 
  1011 		$results = scandir( $path );
  1136 		$results = scandir( $path );
  1012 		$files = array();
  1137 		$files = array();
  1013 
  1138 
       
  1139 		/**
       
  1140 		 * Filters the array of excluded directories and files while scanning theme folder.
       
  1141 		 *
       
  1142 		 * @since 4.7.4
       
  1143 		 *
       
  1144 		 * @param array $exclusions Array of excluded directories and files.
       
  1145 		 */
       
  1146 		$exclusions = (array) apply_filters( 'theme_scandir_exclusions', array( 'CVS', 'node_modules', 'vendor', 'bower_components' ) );
       
  1147 
  1014 		foreach ( $results as $result ) {
  1148 		foreach ( $results as $result ) {
  1015 			if ( '.' == $result[0] )
  1149 			if ( '.' == $result[0] || in_array( $result, $exclusions, true ) ) {
  1016 				continue;
  1150 				continue;
       
  1151 			}
  1017 			if ( is_dir( $path . '/' . $result ) ) {
  1152 			if ( is_dir( $path . '/' . $result ) ) {
  1018 				if ( ! $depth || 'CVS' == $result )
  1153 				if ( ! $depth ) {
  1019 					continue;
  1154 					continue;
       
  1155 				}
  1020 				$found = self::scandir( $path . '/' . $result, $extensions, $depth - 1 , $relative_path . $result );
  1156 				$found = self::scandir( $path . '/' . $result, $extensions, $depth - 1 , $relative_path . $result );
  1021 				$files = array_merge_recursive( $files, $found );
  1157 				$files = array_merge_recursive( $files, $found );
  1022 			} elseif ( ! $extensions || preg_match( '~\.(' . $_extensions . ')$~', $result ) ) {
  1158 			} elseif ( ! $extensions || preg_match( '~\.(' . $_extensions . ')$~', $result ) ) {
  1023 				$files[ $relative_path . $result ] = $path . '/' . $result;
  1159 				$files[ $relative_path . $result ] = $path . '/' . $result;
  1024 			}
  1160 			}
  1032 	 *
  1168 	 *
  1033 	 * Translation files are not inherited from the parent theme. Todo: if this fails for the
  1169 	 * Translation files are not inherited from the parent theme. Todo: if this fails for the
  1034 	 * child theme, it should probably try to load the parent theme's translations.
  1170 	 * child theme, it should probably try to load the parent theme's translations.
  1035 	 *
  1171 	 *
  1036 	 * @since 3.4.0
  1172 	 * @since 3.4.0
  1037 	 * @access public
       
  1038 	 *
  1173 	 *
  1039 	 * @return bool True if the textdomain was successfully loaded or has already been loaded.
  1174 	 * @return bool True if the textdomain was successfully loaded or has already been loaded.
  1040 	 * 	False if no textdomain was specified in the file headers, or if the domain could not be loaded.
  1175 	 * 	False if no textdomain was specified in the file headers, or if the domain could not be loaded.
  1041 	 */
  1176 	 */
  1042 	public function load_textdomain() {
  1177 	public function load_textdomain() {
  1066 
  1201 
  1067 	/**
  1202 	/**
  1068 	 * Whether the theme is allowed (multisite only).
  1203 	 * Whether the theme is allowed (multisite only).
  1069 	 *
  1204 	 *
  1070 	 * @since 3.4.0
  1205 	 * @since 3.4.0
  1071 	 * @access public
       
  1072 	 *
  1206 	 *
  1073 	 * @param string $check Optional. Whether to check only the 'network'-wide settings, the 'site'
  1207 	 * @param string $check Optional. Whether to check only the 'network'-wide settings, the 'site'
  1074 	 * 	settings, or 'both'. Defaults to 'both'.
  1208 	 * 	settings, or 'both'. Defaults to 'both'.
  1075 	 * @param int $blog_id Optional. Ignored if only network-wide settings are checked. Defaults to current blog.
  1209 	 * @param int $blog_id Optional. Ignored if only network-wide settings are checked. Defaults to current site.
  1076 	 * @return bool Whether the theme is allowed for the network. Returns true in single-site.
  1210 	 * @return bool Whether the theme is allowed for the network. Returns true in single-site.
  1077 	 */
  1211 	 */
  1078 	public function is_allowed( $check = 'both', $blog_id = null ) {
  1212 	public function is_allowed( $check = 'both', $blog_id = null ) {
  1079 		if ( ! is_multisite() )
  1213 		if ( ! is_multisite() )
  1080 			return true;
  1214 			return true;
  1093 
  1227 
  1094 		return false;
  1228 		return false;
  1095 	}
  1229 	}
  1096 
  1230 
  1097 	/**
  1231 	/**
       
  1232 	 * Determines the latest WordPress default theme that is installed.
       
  1233 	 *
       
  1234 	 * This hits the filesystem.
       
  1235 	 *
       
  1236 	 * @since  4.4.0
       
  1237 	 *
       
  1238 	 * @return WP_Theme|false Object, or false if no theme is installed, which would be bad.
       
  1239 	 */
       
  1240 	public static function get_core_default_theme() {
       
  1241 		foreach ( array_reverse( self::$default_themes ) as $slug => $name ) {
       
  1242 			$theme = wp_get_theme( $slug );
       
  1243 			if ( $theme->exists() ) {
       
  1244 				return $theme;
       
  1245 			}
       
  1246 		}
       
  1247 		return false;
       
  1248 	}
       
  1249 
       
  1250 	/**
  1098 	 * Returns array of stylesheet names of themes allowed on the site or network.
  1251 	 * Returns array of stylesheet names of themes allowed on the site or network.
  1099 	 *
  1252 	 *
  1100 	 * @since 3.4.0
  1253 	 * @since 3.4.0
  1101 	 * @access public
  1254 	 *
  1102 	 *
  1255 	 * @static
  1103 	 * @param int $blog_id Optional. Defaults to current blog.
  1256 	 *
       
  1257 	 * @param int $blog_id Optional. ID of the site. Defaults to the current site.
  1104 	 * @return array Array of stylesheet names.
  1258 	 * @return array Array of stylesheet names.
  1105 	 */
  1259 	 */
  1106 	public static function get_allowed( $blog_id = null ) {
  1260 	public static function get_allowed( $blog_id = null ) {
  1107 		/**
  1261 		/**
  1108 		 * Filter the array of themes allowed on the site or network.
  1262 		 * Filters the array of themes allowed on the network.
  1109 		 *
  1263 		 *
  1110 		 * @since MU
  1264 		 * Site is provided as context so that a list of network allowed themes can
       
  1265 		 * be filtered further.
       
  1266 		 *
       
  1267 		 * @since 4.5.0
       
  1268 		 *
       
  1269 		 * @param array $allowed_themes An array of theme stylesheet names.
       
  1270 		 * @param int   $blog_id        ID of the site.
       
  1271 		 */
       
  1272 		$network = (array) apply_filters( 'network_allowed_themes', self::get_allowed_on_network(), $blog_id );
       
  1273 		return $network + self::get_allowed_on_site( $blog_id );
       
  1274 	}
       
  1275 
       
  1276 	/**
       
  1277 	 * Returns array of stylesheet names of themes allowed on the network.
       
  1278 	 *
       
  1279 	 * @since 3.4.0
       
  1280 	 *
       
  1281 	 * @static
       
  1282 	 *
       
  1283 	 * @staticvar array $allowed_themes
       
  1284 	 *
       
  1285 	 * @return array Array of stylesheet names.
       
  1286 	 */
       
  1287 	public static function get_allowed_on_network() {
       
  1288 		static $allowed_themes;
       
  1289 		if ( ! isset( $allowed_themes ) ) {
       
  1290 			$allowed_themes = (array) get_site_option( 'allowedthemes' );
       
  1291 		}
       
  1292 
       
  1293 		/**
       
  1294 		 * Filters the array of themes allowed on the network.
       
  1295 		 *
       
  1296 		 * @since MU (3.0.0)
  1111 		 *
  1297 		 *
  1112 		 * @param array $allowed_themes An array of theme stylesheet names.
  1298 		 * @param array $allowed_themes An array of theme stylesheet names.
  1113 		 */
  1299 		 */
  1114 		$network = (array) apply_filters( 'allowed_themes', self::get_allowed_on_network() );
  1300 		$allowed_themes = apply_filters( 'allowed_themes', $allowed_themes );
  1115 		return $network + self::get_allowed_on_site( $blog_id );
  1301 
  1116 	}
       
  1117 
       
  1118 	/**
       
  1119 	 * Returns array of stylesheet names of themes allowed on the network.
       
  1120 	 *
       
  1121 	 * @since 3.4.0
       
  1122 	 * @access public
       
  1123 	 *
       
  1124 	 * @return array Array of stylesheet names.
       
  1125 	 */
       
  1126 	public static function get_allowed_on_network() {
       
  1127 		static $allowed_themes;
       
  1128 		if ( ! isset( $allowed_themes ) )
       
  1129 			$allowed_themes = (array) get_site_option( 'allowedthemes' );
       
  1130 		return $allowed_themes;
  1302 		return $allowed_themes;
  1131 	}
  1303 	}
  1132 
  1304 
  1133 	/**
  1305 	/**
  1134 	 * Returns array of stylesheet names of themes allowed on the site.
  1306 	 * Returns array of stylesheet names of themes allowed on the site.
  1135 	 *
  1307 	 *
  1136 	 * @since 3.4.0
  1308 	 * @since 3.4.0
  1137 	 * @access public
  1309 	 *
  1138 	 *
  1310 	 * @static
  1139 	 * @param int $blog_id Optional. Defaults to current blog.
  1311 	 *
       
  1312 	 * @staticvar array $allowed_themes
       
  1313 	 *
       
  1314 	 * @param int $blog_id Optional. ID of the site. Defaults to the current site.
  1140 	 * @return array Array of stylesheet names.
  1315 	 * @return array Array of stylesheet names.
  1141 	 */
  1316 	 */
  1142 	public static function get_allowed_on_site( $blog_id = null ) {
  1317 	public static function get_allowed_on_site( $blog_id = null ) {
  1143 		static $allowed_themes = array();
  1318 		static $allowed_themes = array();
  1144 
  1319 
  1145 		if ( ! $blog_id || ! is_multisite() )
  1320 		if ( ! $blog_id || ! is_multisite() )
  1146 			$blog_id = get_current_blog_id();
  1321 			$blog_id = get_current_blog_id();
  1147 
  1322 
  1148 		if ( isset( $allowed_themes[ $blog_id ] ) )
  1323 		if ( isset( $allowed_themes[ $blog_id ] ) ) {
  1149 			return $allowed_themes[ $blog_id ];
  1324 			/**
       
  1325 			 * Filters the array of themes allowed on the site.
       
  1326 			 *
       
  1327 			 * @since 4.5.0
       
  1328 			 *
       
  1329 			 * @param array $allowed_themes An array of theme stylesheet names.
       
  1330 			 * @param int   $blog_id        ID of the site. Defaults to current site.
       
  1331 			 */
       
  1332 			return (array) apply_filters( 'site_allowed_themes', $allowed_themes[ $blog_id ], $blog_id );
       
  1333 		}
  1150 
  1334 
  1151 		$current = $blog_id == get_current_blog_id();
  1335 		$current = $blog_id == get_current_blog_id();
  1152 
  1336 
  1153 		if ( $current ) {
  1337 		if ( $current ) {
  1154 			$allowed_themes[ $blog_id ] = get_option( 'allowedthemes' );
  1338 			$allowed_themes[ $blog_id ] = get_option( 'allowedthemes' );
  1192 					restore_current_blog();
  1376 					restore_current_blog();
  1193 				}
  1377 				}
  1194 			}
  1378 			}
  1195 		}
  1379 		}
  1196 
  1380 
  1197 		return (array) $allowed_themes[ $blog_id ];
  1381 		/** This filter is documented in wp-includes/class-wp-theme.php */
  1198 	}
  1382 		return (array) apply_filters( 'site_allowed_themes', $allowed_themes[ $blog_id ], $blog_id );
  1199 
  1383 	}
  1200 	/**
  1384 
  1201 	 * Sort themes by name.
  1385 	/**
  1202 	 *
  1386 	 * Enables a theme for all sites on the current network.
  1203 	 * @since 3.4.0
  1387 	 *
  1204 	 * @access public
  1388 	 * @since 4.6.0
       
  1389 	 * @static
       
  1390 	 *
       
  1391 	 * @param string|array $stylesheets Stylesheet name or array of stylesheet names.
       
  1392 	 */
       
  1393 	public static function network_enable_theme( $stylesheets ) {
       
  1394 		if ( ! is_multisite() ) {
       
  1395 			return;
       
  1396 		}
       
  1397 
       
  1398 		if ( ! is_array( $stylesheets ) ) {
       
  1399 			$stylesheets = array( $stylesheets );
       
  1400 		}
       
  1401 
       
  1402 		$allowed_themes = get_site_option( 'allowedthemes' );
       
  1403 		foreach ( $stylesheets as $stylesheet ) {
       
  1404 			$allowed_themes[ $stylesheet ] = true;
       
  1405 		}
       
  1406 
       
  1407 		update_site_option( 'allowedthemes', $allowed_themes );
       
  1408 	}
       
  1409 
       
  1410 	/**
       
  1411 	 * Disables a theme for all sites on the current network.
       
  1412 	 *
       
  1413 	 * @since 4.6.0
       
  1414 	 * @static
       
  1415 	 *
       
  1416 	 * @param string|array $stylesheets Stylesheet name or array of stylesheet names.
       
  1417 	 */
       
  1418 	public static function network_disable_theme( $stylesheets ) {
       
  1419 		if ( ! is_multisite() ) {
       
  1420 			return;
       
  1421 		}
       
  1422 
       
  1423 		if ( ! is_array( $stylesheets ) ) {
       
  1424 			$stylesheets = array( $stylesheets );
       
  1425 		}
       
  1426 
       
  1427 		$allowed_themes = get_site_option( 'allowedthemes' );
       
  1428 		foreach ( $stylesheets as $stylesheet ) {
       
  1429 			if ( isset( $allowed_themes[ $stylesheet ] ) ) {
       
  1430 				unset( $allowed_themes[ $stylesheet ] );
       
  1431 			}
       
  1432 		}
       
  1433 
       
  1434 		update_site_option( 'allowedthemes', $allowed_themes );
       
  1435 	}
       
  1436 
       
  1437 	/**
       
  1438 	 * Sorts themes by name.
       
  1439 	 *
       
  1440 	 * @since 3.4.0
       
  1441 	 *
       
  1442 	 * @static
       
  1443 	 *
       
  1444 	 * @param array $themes Array of themes to sort (passed by reference).
  1205 	 */
  1445 	 */
  1206 	public static function sort_by_name( &$themes ) {
  1446 	public static function sort_by_name( &$themes ) {
  1207 		if ( 0 === strpos( get_locale(), 'en_' ) ) {
  1447 		if ( 0 === strpos( get_user_locale(), 'en_' ) ) {
  1208 			uasort( $themes, array( 'WP_Theme', '_name_sort' ) );
  1448 			uasort( $themes, array( 'WP_Theme', '_name_sort' ) );
  1209 		} else {
  1449 		} else {
  1210 			uasort( $themes, array( 'WP_Theme', '_name_sort_i18n' ) );
  1450 			uasort( $themes, array( 'WP_Theme', '_name_sort_i18n' ) );
  1211 		}
  1451 		}
  1212 	}
  1452 	}
  1216 	 *
  1456 	 *
  1217 	 * Accesses the Name header directly from the class for maximum speed.
  1457 	 * Accesses the Name header directly from the class for maximum speed.
  1218 	 * Would choke on HTML but we don't care enough to slow it down with strip_tags().
  1458 	 * Would choke on HTML but we don't care enough to slow it down with strip_tags().
  1219 	 *
  1459 	 *
  1220 	 * @since 3.4.0
  1460 	 * @since 3.4.0
  1221 	 * @access private
  1461 	 *
       
  1462 	 * @static
       
  1463 	 *
       
  1464 	 * @param string $a First name.
       
  1465 	 * @param string $b Second name.
       
  1466 	 * @return int Negative if `$a` falls lower in the natural order than `$b`. Zero if they fall equally.
       
  1467 	 *             Greater than 0 if `$a` falls higher in the natural order than `$b`. Used with usort().
  1222 	 */
  1468 	 */
  1223 	private static function _name_sort( $a, $b ) {
  1469 	private static function _name_sort( $a, $b ) {
  1224 		return strnatcasecmp( $a->headers['Name'], $b->headers['Name'] );
  1470 		return strnatcasecmp( $a->headers['Name'], $b->headers['Name'] );
  1225 	}
  1471 	}
  1226 
  1472 
  1227 	/**
  1473 	/**
  1228 	 * Name sort (with translation).
  1474 	 * Name sort (with translation).
  1229 	 *
  1475 	 *
  1230 	 * @since 3.4.0
  1476 	 * @since 3.4.0
  1231 	 * @access private
  1477 	 *
       
  1478 	 * @static
       
  1479 	 *
       
  1480 	 * @param string $a First name.
       
  1481 	 * @param string $b Second name.
       
  1482 	 * @return int Negative if `$a` falls lower in the natural order than `$b`. Zero if they fall equally.
       
  1483 	 *             Greater than 0 if `$a` falls higher in the natural order than `$b`. Used with usort().
  1232 	 */
  1484 	 */
  1233 	private static function _name_sort_i18n( $a, $b ) {
  1485 	private static function _name_sort_i18n( $a, $b ) {
  1234 		// Don't mark up; Do translate.
  1486 		// Don't mark up; Do translate.
  1235 		return strnatcasecmp( $a->display( 'Name', false, true ), $b->display( 'Name', false, true ) );
  1487 		return strnatcasecmp( $a->display( 'Name', false, true ), $b->display( 'Name', false, true ) );
  1236 	}
  1488 	}