]> scripts.mit.edu Git - autoinstalls/wordpress.git/blob - wp-includes/theme.php
Wordpress 3.3-scripts2
[autoinstalls/wordpress.git] / wp-includes / theme.php
1 <?php
2 /**
3  * Theme, template, and stylesheet functions.
4  *
5  * @package WordPress
6  * @subpackage Template
7  */
8
9 /**
10  * Whether a child theme is in use.
11  *
12  * @since 3.0.0
13  *
14  * @return bool true if a child theme is in use, false otherwise.
15  **/
16 function is_child_theme() {
17         return ( TEMPLATEPATH !== STYLESHEETPATH );
18 }
19
20 /**
21  * Retrieve name of the current stylesheet.
22  *
23  * The theme name that the administrator has currently set the front end theme
24  * as.
25  *
26  * For all extensive purposes, the template name and the stylesheet name are
27  * going to be the same for most cases.
28  *
29  * @since 1.5.0
30  * @uses apply_filters() Calls 'stylesheet' filter on stylesheet name.
31  *
32  * @return string Stylesheet name.
33  */
34 function get_stylesheet() {
35         return apply_filters('stylesheet', get_option('stylesheet'));
36 }
37
38 /**
39  * Retrieve stylesheet directory path for current theme.
40  *
41  * @since 1.5.0
42  * @uses apply_filters() Calls 'stylesheet_directory' filter on stylesheet directory and theme name.
43  *
44  * @return string Path to current theme directory.
45  */
46 function get_stylesheet_directory() {
47         $stylesheet = get_stylesheet();
48         $theme_root = get_theme_root( $stylesheet );
49         $stylesheet_dir = "$theme_root/$stylesheet";
50
51         return apply_filters( 'stylesheet_directory', $stylesheet_dir, $stylesheet, $theme_root );
52 }
53
54 /**
55  * Retrieve stylesheet directory URI.
56  *
57  * @since 1.5.0
58  *
59  * @return string
60  */
61 function get_stylesheet_directory_uri() {
62         $stylesheet = get_stylesheet();
63         $theme_root_uri = get_theme_root_uri( $stylesheet );
64         $stylesheet_dir_uri = "$theme_root_uri/$stylesheet";
65
66         return apply_filters( 'stylesheet_directory_uri', $stylesheet_dir_uri, $stylesheet, $theme_root_uri );
67 }
68
69 /**
70  * Retrieve URI of current theme stylesheet.
71  *
72  * The stylesheet file name is 'style.css' which is appended to {@link
73  * get_stylesheet_directory_uri() stylesheet directory URI} path.
74  *
75  * @since 1.5.0
76  * @uses apply_filters() Calls 'stylesheet_uri' filter on stylesheet URI path and stylesheet directory URI.
77  *
78  * @return string
79  */
80 function get_stylesheet_uri() {
81         $stylesheet_dir_uri = get_stylesheet_directory_uri();
82         $stylesheet_uri = $stylesheet_dir_uri . '/style.css';
83         return apply_filters('stylesheet_uri', $stylesheet_uri, $stylesheet_dir_uri);
84 }
85
86 /**
87  * Retrieve localized stylesheet URI.
88  *
89  * The stylesheet directory for the localized stylesheet files are located, by
90  * default, in the base theme directory. The name of the locale file will be the
91  * locale followed by '.css'. If that does not exist, then the text direction
92  * stylesheet will be checked for existence, for example 'ltr.css'.
93  *
94  * The theme may change the location of the stylesheet directory by either using
95  * the 'stylesheet_directory_uri' filter or the 'locale_stylesheet_uri' filter.
96  * If you want to change the location of the stylesheet files for the entire
97  * WordPress workflow, then change the former. If you just have the locale in a
98  * separate folder, then change the latter.
99  *
100  * @since 2.1.0
101  * @uses apply_filters() Calls 'locale_stylesheet_uri' filter on stylesheet URI path and stylesheet directory URI.
102  *
103  * @return string
104  */
105 function get_locale_stylesheet_uri() {
106         global $wp_locale;
107         $stylesheet_dir_uri = get_stylesheet_directory_uri();
108         $dir = get_stylesheet_directory();
109         $locale = get_locale();
110         if ( file_exists("$dir/$locale.css") )
111                 $stylesheet_uri = "$stylesheet_dir_uri/$locale.css";
112         elseif ( !empty($wp_locale->text_direction) && file_exists("$dir/{$wp_locale->text_direction}.css") )
113                 $stylesheet_uri = "$stylesheet_dir_uri/{$wp_locale->text_direction}.css";
114         else
115                 $stylesheet_uri = '';
116         return apply_filters('locale_stylesheet_uri', $stylesheet_uri, $stylesheet_dir_uri);
117 }
118
119 /**
120  * Retrieve name of the current theme.
121  *
122  * @since 1.5.0
123  * @uses apply_filters() Calls 'template' filter on template option.
124  *
125  * @return string Template name.
126  */
127 function get_template() {
128         return apply_filters('template', get_option('template'));
129 }
130
131 /**
132  * Retrieve current theme directory.
133  *
134  * @since 1.5.0
135  * @uses apply_filters() Calls 'template_directory' filter on template directory path and template name.
136  *
137  * @return string Template directory path.
138  */
139 function get_template_directory() {
140         $template = get_template();
141         $theme_root = get_theme_root( $template );
142         $template_dir = "$theme_root/$template";
143
144         return apply_filters( 'template_directory', $template_dir, $template, $theme_root );
145 }
146
147 /**
148  * Retrieve theme directory URI.
149  *
150  * @since 1.5.0
151  * @uses apply_filters() Calls 'template_directory_uri' filter on template directory URI path and template name.
152  *
153  * @return string Template directory URI.
154  */
155 function get_template_directory_uri() {
156         $template = get_template();
157         $theme_root_uri = get_theme_root_uri( $template );
158         $template_dir_uri = "$theme_root_uri/$template";
159
160         return apply_filters( 'template_directory_uri', $template_dir_uri, $template, $theme_root_uri );
161 }
162
163 /**
164  * Retrieve theme data from parsed theme file.
165  *
166  * The description will have the tags filtered with the following HTML elements
167  * whitelisted. The <b>'a'</b> element with the <em>href</em> and <em>title</em>
168  * attributes. The <b>abbr</b> element with the <em>title</em> attribute. The
169  * <b>acronym</b> element with the <em>title</em> attribute allowed. The
170  * <b>code</b>, <b>em</b>, and <b>strong</b> elements also allowed.
171  *
172  * The style.css file must contain theme name, theme URI, and description. The
173  * data can also contain author URI, author, template (parent template),
174  * version, status, and finally tags. Some of these are not used by WordPress
175  * administration panels, but are used by theme directory web sites which list
176  * the theme.
177  *
178  * @since 1.5.0
179  *
180  * @param string $theme_file Theme file path.
181  * @return array Theme data.
182  */
183 function get_theme_data( $theme_file ) {
184         $default_headers = array(
185                 'Name' => 'Theme Name',
186                 'URI' => 'Theme URI',
187                 'Description' => 'Description',
188                 'Author' => 'Author',
189                 'AuthorURI' => 'Author URI',
190                 'Version' => 'Version',
191                 'Template' => 'Template',
192                 'Status' => 'Status',
193                 'Tags' => 'Tags'
194                 );
195
196         $themes_allowed_tags = array(
197                 'a' => array(
198                         'href' => array(),'title' => array()
199                         ),
200                 'abbr' => array(
201                         'title' => array()
202                         ),
203                 'acronym' => array(
204                         'title' => array()
205                         ),
206                 'code' => array(),
207                 'em' => array(),
208                 'strong' => array()
209         );
210
211         $theme_data = get_file_data( $theme_file, $default_headers, 'theme' );
212
213         $theme_data['Name'] = $theme_data['Title'] = wp_kses( $theme_data['Name'], $themes_allowed_tags );
214
215         $theme_data['URI'] = esc_url( $theme_data['URI'] );
216
217         $theme_data['Description'] = wptexturize( wp_kses( $theme_data['Description'], $themes_allowed_tags ) );
218
219         $theme_data['AuthorURI'] = esc_url( $theme_data['AuthorURI'] );
220
221         $theme_data['Template'] = wp_kses( $theme_data['Template'], $themes_allowed_tags );
222
223         $theme_data['Version'] = wp_kses( $theme_data['Version'], $themes_allowed_tags );
224
225         if ( $theme_data['Status'] == '' )
226                 $theme_data['Status'] = 'publish';
227         else
228                 $theme_data['Status'] = wp_kses( $theme_data['Status'], $themes_allowed_tags );
229
230         if ( $theme_data['Tags'] == '' )
231                 $theme_data['Tags'] = array();
232         else
233                 $theme_data['Tags'] = array_map( 'trim', explode( ',', wp_kses( $theme_data['Tags'], array() ) ) );
234
235         if ( $theme_data['Author'] == '' ) {
236                 $theme_data['Author'] = $theme_data['AuthorName'] = __('Anonymous');
237         } else {
238                 $theme_data['AuthorName'] = wp_kses( $theme_data['Author'], $themes_allowed_tags );
239                 if ( empty( $theme_data['AuthorURI'] ) ) {
240                         $theme_data['Author'] = $theme_data['AuthorName'];
241                 } else {
242                         $theme_data['Author'] = sprintf( '<a href="%1$s" title="%2$s">%3$s</a>', $theme_data['AuthorURI'], esc_attr__( 'Visit author homepage' ), $theme_data['AuthorName'] );
243                 }
244         }
245
246         return $theme_data;
247 }
248
249 /**
250  * Retrieve list of themes with theme data in theme directory.
251  *
252  * The theme is broken, if it doesn't have a parent theme and is missing either
253  * style.css and, or index.php. If the theme has a parent theme then it is
254  * broken, if it is missing style.css; index.php is optional. The broken theme
255  * list is saved in the {@link $wp_broken_themes} global, which is displayed on
256  * the theme list in the administration panels.
257  *
258  * @since 1.5.0
259  * @global array $wp_broken_themes Stores the broken themes.
260  * @global array $wp_themes Stores the working themes.
261  *
262  * @return array Theme list with theme data.
263  */
264 function get_themes() {
265         global $wp_themes, $wp_broken_themes;
266
267         if ( isset($wp_themes) )
268                 return $wp_themes;
269
270         if ( !$theme_files = search_theme_directories() )
271                 return false;
272
273         asort( $theme_files );
274
275         $wp_themes = array();
276
277         foreach ( (array) $theme_files as $theme_file ) {
278                 $theme_root = $theme_file['theme_root'];
279                 $theme_file = $theme_file['theme_file'];
280
281                 if ( !is_readable("$theme_root/$theme_file") ) {
282                         $wp_broken_themes[$theme_file] = array('Name' => $theme_file, 'Title' => $theme_file, 'Description' => __('File not readable.'));
283                         continue;
284                 }
285
286                 $theme_data = get_theme_data("$theme_root/$theme_file");
287
288                 $name        = $theme_data['Name'];
289                 $title       = $theme_data['Title'];
290                 $description = wptexturize($theme_data['Description']);
291                 $version     = $theme_data['Version'];
292                 $author      = $theme_data['Author'];
293                 $template    = $theme_data['Template'];
294                 $stylesheet  = dirname($theme_file);
295
296                 $screenshot = false;
297                 foreach ( array('png', 'gif', 'jpg', 'jpeg') as $ext ) {
298                         if (file_exists("$theme_root/$stylesheet/screenshot.$ext")) {
299                                 $screenshot = "screenshot.$ext";
300                                 break;
301                         }
302                 }
303
304                 if ( empty($name) ) {
305                         $name = dirname($theme_file);
306                         $title = $name;
307                 }
308
309                 $parent_template = $template;
310
311                 if ( empty($template) ) {
312                         if ( file_exists("$theme_root/$stylesheet/index.php") )
313                                 $template = $stylesheet;
314                         else
315                                 continue;
316                 }
317
318                 $template = trim( $template );
319
320                 if ( !file_exists("$theme_root/$template/index.php") ) {
321                         $parent_dir = dirname(dirname($theme_file));
322                         if ( file_exists("$theme_root/$parent_dir/$template/index.php") ) {
323                                 $template = "$parent_dir/$template";
324                                 $template_directory = "$theme_root/$template";
325                         } else {
326                                 /**
327                                  * The parent theme doesn't exist in the current theme's folder or sub folder
328                                  * so lets use the theme root for the parent template.
329                                  */
330                                 if ( isset($theme_files[$template]) && file_exists( $theme_files[$template]['theme_root'] . "/$template/index.php" ) ) {
331                                         $template_directory = $theme_files[$template]['theme_root'] . "/$template";
332                                 } else {
333                                         if ( empty( $parent_template) )
334                                                 $wp_broken_themes[$name] = array('Name' => $name, 'Title' => $title, 'Description' => __('Template is missing.'), 'error' => 'no_template');
335                                         else
336                                                 $wp_broken_themes[$name] = array('Name' => $name, 'Title' => $title, 'Description' => sprintf( __('The parent theme is missing. Please install the "%s" parent theme.'),  $parent_template ), 'error' => 'no_parent', 'parent' => $parent_template );
337                                         continue;
338                                 }
339
340                         }
341                 } else {
342                         $template_directory = trim( $theme_root . '/' . $template );
343                 }
344
345                 $stylesheet_files = array();
346                 $template_files = array();
347
348                 $stylesheet_dir = @ dir("$theme_root/$stylesheet");
349                 if ( $stylesheet_dir ) {
350                         while ( ($file = $stylesheet_dir->read()) !== false ) {
351                                 if ( !preg_match('|^\.+$|', $file) ) {
352                                         if ( preg_match('|\.css$|', $file) )
353                                                 $stylesheet_files[] = "$theme_root/$stylesheet/$file";
354                                         elseif ( preg_match('|\.php$|', $file) )
355                                                 $template_files[] = "$theme_root/$stylesheet/$file";
356                                 }
357                         }
358                         @ $stylesheet_dir->close();
359                 }
360
361                 $template_dir = @ dir("$template_directory");
362                 if ( $template_dir ) {
363                         while ( ($file = $template_dir->read()) !== false ) {
364                                 if ( preg_match('|^\.+$|', $file) )
365                                         continue;
366                                 if ( preg_match('|\.php$|', $file) ) {
367                                         $template_files[] = "$template_directory/$file";
368                                 } elseif ( is_dir("$template_directory/$file") ) {
369                                         $template_subdir = @ dir("$template_directory/$file");
370                                         if ( !$template_subdir )
371                                                 continue;
372                                         while ( ($subfile = $template_subdir->read()) !== false ) {
373                                                 if ( preg_match('|^\.+$|', $subfile) )
374                                                         continue;
375                                                 if ( preg_match('|\.php$|', $subfile) )
376                                                         $template_files[] = "$template_directory/$file/$subfile";
377                                         }
378                                         @ $template_subdir->close();
379                                 }
380                         }
381                         @ $template_dir->close();
382                 }
383
384                 //Make unique and remove duplicates when stylesheet and template are the same i.e. most themes
385                 $template_files = array_unique($template_files);
386                 $stylesheet_files = array_unique($stylesheet_files);
387
388                 $template_dir = $template_directory;
389                 $stylesheet_dir = $theme_root . '/' . $stylesheet;
390
391                 if ( empty($template_dir) )
392                         $template_dir = '/';
393                 if ( empty($stylesheet_dir) )
394                         $stylesheet_dir = '/';
395
396                 // Check for theme name collision.  This occurs if a theme is copied to
397                 // a new theme directory and the theme header is not updated.  Whichever
398                 // theme is first keeps the name.  Subsequent themes get a suffix applied.
399                 // The Twenty Eleven, Twenty Ten, Default and Classic themes always trump
400                 // their pretenders.
401                 if ( isset($wp_themes[$name]) ) {
402                         $trump_cards = array(
403                                 'classic'      => 'WordPress Classic',
404                                 'default'      => 'WordPress Default',
405                                 'twentyten'    => 'Twenty Ten',
406                                 'twentyeleven' => 'Twenty Eleven',
407                         );
408                         if ( isset( $trump_cards[ $stylesheet ] ) && $name == $trump_cards[ $stylesheet ] ) {
409                                 // If another theme has claimed to be one of our default themes, move
410                                 // them aside.
411                                 $suffix = $wp_themes[$name]['Stylesheet'];
412                                 $new_name = "$name/$suffix";
413                                 $wp_themes[$new_name] = $wp_themes[$name];
414                                 $wp_themes[$new_name]['Name'] = $new_name;
415                         } else {
416                                 $name = "$name/$stylesheet";
417                         }
418                 }
419
420                 $theme_roots[$stylesheet] = str_replace( WP_CONTENT_DIR, '', $theme_root );
421                 $wp_themes[$name] = array(
422                         'Name' => $name,
423                         'Title' => $title,
424                         'Description' => $description,
425                         'Author' => $author,
426                         'Author Name' => $theme_data['AuthorName'],
427                         'Author URI' => $theme_data['AuthorURI'],
428                         'Version' => $version,
429                         'Template' => $template,
430                         'Stylesheet' => $stylesheet,
431                         'Template Files' => $template_files,
432                         'Stylesheet Files' => $stylesheet_files,
433                         'Template Dir' => $template_dir,
434                         'Stylesheet Dir' => $stylesheet_dir,
435                         'Status' => $theme_data['Status'],
436                         'Screenshot' => $screenshot,
437                         'Tags' => $theme_data['Tags'],
438                         'Theme Root' => $theme_root,
439                         'Theme Root URI' => str_replace( WP_CONTENT_DIR, content_url(), $theme_root ),
440                 );
441         }
442
443         unset($theme_files);
444
445         /* Store theme roots in the DB */
446         if ( get_site_transient( 'theme_roots' ) != $theme_roots )
447                 set_site_transient( 'theme_roots', $theme_roots, 7200 ); // cache for two hours
448         unset($theme_roots);
449
450         /* Resolve theme dependencies. */
451         $theme_names = array_keys( $wp_themes );
452         foreach ( (array) $theme_names as $theme_name ) {
453                 $wp_themes[$theme_name]['Parent Theme'] = '';
454                 if ( $wp_themes[$theme_name]['Stylesheet'] != $wp_themes[$theme_name]['Template'] ) {
455                         foreach ( (array) $theme_names as $parent_theme_name ) {
456                                 if ( ($wp_themes[$parent_theme_name]['Stylesheet'] == $wp_themes[$parent_theme_name]['Template']) && ($wp_themes[$parent_theme_name]['Template'] == $wp_themes[$theme_name]['Template']) ) {
457                                         $wp_themes[$theme_name]['Parent Theme'] = $wp_themes[$parent_theme_name]['Name'];
458                                         break;
459                                 }
460                         }
461                 }
462         }
463
464         return $wp_themes;
465 }
466
467 /**
468  * Retrieve theme roots.
469  *
470  * @since 2.9.0
471  *
472  * @return array|string An array of theme roots keyed by template/stylesheet or a single theme root if all themes have the same root.
473  */
474 function get_theme_roots() {
475         global $wp_theme_directories;
476
477         if ( count($wp_theme_directories) <= 1 )
478                 return '/themes';
479
480         $theme_roots = get_site_transient( 'theme_roots' );
481         if ( false === $theme_roots ) {
482                 get_themes();
483                 $theme_roots = get_site_transient( 'theme_roots' ); // this is set in get_theme()
484         }
485         return $theme_roots;
486 }
487
488 /**
489  * Retrieve theme data.
490  *
491  * @since 1.5.0
492  *
493  * @param string $theme Theme name.
494  * @return array|null Null, if theme name does not exist. Theme data, if exists.
495  */
496 function get_theme($theme) {
497         $themes = get_themes();
498
499         if ( is_array( $themes ) && array_key_exists( $theme, $themes ) )
500                 return $themes[$theme];
501
502         return null;
503 }
504
505 /**
506  * Retrieve current theme display name.
507  *
508  * If the 'current_theme' option has already been set, then it will be returned
509  * instead. If it is not set, then each theme will be iterated over until both
510  * the current stylesheet and current template name.
511  *
512  * @since 1.5.0
513  *
514  * @return string
515  */
516 function get_current_theme() {
517         if ( $theme = get_option('current_theme') )
518                 return $theme;
519
520         $themes = get_themes();
521         $current_theme = 'Twenty Eleven';
522
523         if ( $themes ) {
524                 $theme_names = array_keys( $themes );
525                 $current_template = get_option( 'template' );
526                 $current_stylesheet = get_option( 'stylesheet' );
527
528                 foreach ( (array) $theme_names as $theme_name ) {
529                         if ( $themes[$theme_name]['Stylesheet'] == $current_stylesheet &&
530                                         $themes[$theme_name]['Template'] == $current_template ) {
531                                 $current_theme = $themes[$theme_name]['Name'];
532                                 break;
533                         }
534                 }
535         }
536
537         update_option('current_theme', $current_theme);
538
539         return $current_theme;
540 }
541
542 /**
543  * Register a directory that contains themes.
544  *
545  * @since 2.9.0
546  *
547  * @param string $directory Either the full filesystem path to a theme folder or a folder within WP_CONTENT_DIR
548  * @return bool
549  */
550 function register_theme_directory( $directory) {
551         global $wp_theme_directories;
552
553         /* If this folder does not exist, return and do not register */
554         if ( !file_exists( $directory ) )
555                         /* Try prepending as the theme directory could be relative to the content directory */
556                 $registered_directory = WP_CONTENT_DIR . '/' . $directory;
557         else
558                 $registered_directory = $directory;
559
560         /* If this folder does not exist, return and do not register */
561         if ( !file_exists( $registered_directory ) )
562                 return false;
563
564         $wp_theme_directories[] = $registered_directory;
565
566         return true;
567 }
568
569 /**
570  * Search all registered theme directories for complete and valid themes.
571  *
572  * @since 2.9.0
573  *
574  * @return array Valid themes found
575  */
576 function search_theme_directories() {
577         global $wp_theme_directories, $wp_broken_themes;
578         if ( empty( $wp_theme_directories ) )
579                 return false;
580
581         $theme_files = array();
582         $wp_broken_themes = array();
583
584         /* Loop the registered theme directories and extract all themes */
585         foreach ( (array) $wp_theme_directories as $theme_root ) {
586                 $theme_loc = $theme_root;
587
588                 /* We don't want to replace all forward slashes, see Trac #4541 */
589                 if ( '/' != WP_CONTENT_DIR )
590                         $theme_loc = str_replace(WP_CONTENT_DIR, '', $theme_root);
591
592                 /* Files in the root of the current theme directory and one subdir down */
593                 $themes_dir = @ opendir($theme_root);
594
595                 if ( !$themes_dir )
596                         return false;
597
598                 while ( ($theme_dir = readdir($themes_dir)) !== false ) {
599                         if ( is_dir($theme_root . '/' . $theme_dir) && is_readable($theme_root . '/' . $theme_dir) ) {
600                                 if ( $theme_dir[0] == '.' || $theme_dir == 'CVS' )
601                                         continue;
602
603                                 $stylish_dir = @opendir($theme_root . '/' . $theme_dir);
604                                 $found_stylesheet = false;
605
606                                 while ( ($theme_file = readdir($stylish_dir)) !== false ) {
607                                         if ( $theme_file == 'style.css' ) {
608                                                 $theme_files[$theme_dir] = array( 'theme_file' => $theme_dir . '/' . $theme_file, 'theme_root' => $theme_root );
609                                                 $found_stylesheet = true;
610                                                 break;
611                                         }
612                                 }
613                                 @closedir($stylish_dir);
614
615                                 if ( !$found_stylesheet ) { // look for themes in that dir
616                                         $subdir = "$theme_root/$theme_dir";
617                                         $subdir_name = $theme_dir;
618                                         $theme_subdirs = @opendir( $subdir );
619
620                                         $found_subdir_themes = false;
621                                         while ( ($theme_subdir = readdir($theme_subdirs)) !== false ) {
622                                                 if ( is_dir( $subdir . '/' . $theme_subdir) && is_readable($subdir . '/' . $theme_subdir) ) {
623                                                         if ( $theme_subdir[0] == '.' || $theme_subdir == 'CVS' )
624                                                                 continue;
625
626                                                         $stylish_dir = @opendir($subdir . '/' . $theme_subdir);
627                                                         $found_stylesheet = false;
628
629                                                         while ( ($theme_file = readdir($stylish_dir)) !== false ) {
630                                                                 if ( $theme_file == 'style.css' ) {
631                                                                         $theme_files["$theme_dir/$theme_subdir"] = array( 'theme_file' => $subdir_name . '/' . $theme_subdir . '/' . $theme_file, 'theme_root' => $theme_root );
632                                                                         $found_stylesheet = true;
633                                                                         $found_subdir_themes = true;
634                                                                         break;
635                                                                 }
636                                                         }
637                                                         @closedir($stylish_dir);
638                                                 }
639                                         }
640                                         @closedir($theme_subdirs);
641                                         if ( !$found_subdir_themes )
642                                                 $wp_broken_themes[$theme_dir] = array('Name' => $theme_dir, 'Title' => $theme_dir, 'Description' => __('Stylesheet is missing.'));
643                                 }
644                         }
645                 }
646                 @closedir( $themes_dir );
647         }
648         return $theme_files;
649 }
650
651 /**
652  * Retrieve path to themes directory.
653  *
654  * Does not have trailing slash.
655  *
656  * @since 1.5.0
657  * @uses apply_filters() Calls 'theme_root' filter on path.
658  *
659  * @param string $stylesheet_or_template The stylesheet or template name of the theme
660  * @return string Theme path.
661  */
662 function get_theme_root( $stylesheet_or_template = false ) {
663         if ( $stylesheet_or_template ) {
664                 if ( $theme_root = get_raw_theme_root($stylesheet_or_template) )
665                         $theme_root = WP_CONTENT_DIR . $theme_root;
666                 else
667                         $theme_root = WP_CONTENT_DIR . '/themes';
668         } else {
669                 $theme_root = WP_CONTENT_DIR . '/themes';
670         }
671
672         return apply_filters( 'theme_root', $theme_root );
673 }
674
675 /**
676  * Retrieve URI for themes directory.
677  *
678  * Does not have trailing slash.
679  *
680  * @since 1.5.0
681  *
682  * @param string $stylesheet_or_template The stylesheet or template name of the theme
683  * @return string Themes URI.
684  */
685 function get_theme_root_uri( $stylesheet_or_template = false ) {
686         if ( $stylesheet_or_template ) {
687                 if ( $theme_root = get_raw_theme_root($stylesheet_or_template) )
688                         $theme_root_uri = content_url( $theme_root );
689                 else
690                         $theme_root_uri = content_url( 'themes' );
691         } else {
692                 $theme_root_uri = content_url( 'themes' );
693         }
694
695         return apply_filters( 'theme_root_uri', $theme_root_uri, get_option('siteurl'), $stylesheet_or_template );
696 }
697
698 /**
699  * Get the raw theme root relative to the content directory with no filters applied.
700  *
701  * @since 3.1.0
702  *
703  * @param string $stylesheet_or_template The stylesheet or template name of the theme
704  * @return string Theme root
705  */
706 function get_raw_theme_root( $stylesheet_or_template, $no_cache = false ) {
707         global $wp_theme_directories;
708
709         if ( count($wp_theme_directories) <= 1 )
710                 return '/themes';
711
712         $theme_root = false;
713
714         // If requesting the root for the current theme, consult options to avoid calling get_theme_roots()
715         if ( !$no_cache ) {
716                 if ( get_option('stylesheet') == $stylesheet_or_template )
717                         $theme_root = get_option('stylesheet_root');
718                 elseif ( get_option('template') == $stylesheet_or_template )
719                         $theme_root = get_option('template_root');
720         }
721
722         if ( empty($theme_root) ) {
723                 $theme_roots = get_theme_roots();
724                 if ( !empty($theme_roots[$stylesheet_or_template]) )
725                         $theme_root = $theme_roots[$stylesheet_or_template];
726         }
727
728         return $theme_root;
729 }
730
731 /**
732  * Retrieve path to a template
733  *
734  * Used to quickly retrieve the path of a template without including the file
735  * extension. It will also check the parent theme, if the file exists, with
736  * the use of {@link locate_template()}. Allows for more generic template location
737  * without the use of the other get_*_template() functions.
738  *
739  * @since 1.5.0
740  *
741  * @param string $type Filename without extension.
742  * @param array $templates An optional list of template candidates
743  * @return string Full path to file.
744  */
745 function get_query_template( $type, $templates = array() ) {
746         $type = preg_replace( '|[^a-z0-9-]+|', '', $type );
747
748         if ( empty( $templates ) )
749                 $templates = array("{$type}.php");
750
751         return apply_filters( "{$type}_template", locate_template( $templates ) );
752 }
753
754 /**
755  * Retrieve path of index template in current or parent template.
756  *
757  * @since 3.0.0
758  *
759  * @return string
760  */
761 function get_index_template() {
762         return get_query_template('index');
763 }
764
765 /**
766  * Retrieve path of 404 template in current or parent template.
767  *
768  * @since 1.5.0
769  *
770  * @return string
771  */
772 function get_404_template() {
773         return get_query_template('404');
774 }
775
776 /**
777  * Retrieve path of archive template in current or parent template.
778  *
779  * @since 1.5.0
780  *
781  * @return string
782  */
783 function get_archive_template() {
784         $post_type = get_query_var( 'post_type' );
785
786         $templates = array();
787
788         if ( $post_type )
789                 $templates[] = "archive-{$post_type}.php";
790         $templates[] = 'archive.php';
791
792         return get_query_template( 'archive', $templates );
793 }
794
795 /**
796  * Retrieve path of author template in current or parent template.
797  *
798  * @since 1.5.0
799  *
800  * @return string
801  */
802 function get_author_template() {
803         $author = get_queried_object();
804
805         $templates = array();
806
807         $templates[] = "author-{$author->user_nicename}.php";
808         $templates[] = "author-{$author->ID}.php";
809         $templates[] = 'author.php';
810
811         return get_query_template( 'author', $templates );
812 }
813
814 /**
815  * Retrieve path of category template in current or parent template.
816  *
817  * Works by first retrieving the current slug for example 'category-default.php' and then
818  * trying category ID, for example 'category-1.php' and will finally fallback to category.php
819  * template, if those files don't exist.
820  *
821  * @since 1.5.0
822  * @uses apply_filters() Calls 'category_template' on file path of category template.
823  *
824  * @return string
825  */
826 function get_category_template() {
827         $category = get_queried_object();
828
829         $templates = array();
830
831         $templates[] = "category-{$category->slug}.php";
832         $templates[] = "category-{$category->term_id}.php";
833         $templates[] = 'category.php';
834
835         return get_query_template( 'category', $templates );
836 }
837
838 /**
839  * Retrieve path of tag template in current or parent template.
840  *
841  * Works by first retrieving the current tag name, for example 'tag-wordpress.php' and then
842  * trying tag ID, for example 'tag-1.php' and will finally fallback to tag.php
843  * template, if those files don't exist.
844  *
845  * @since 2.3.0
846  * @uses apply_filters() Calls 'tag_template' on file path of tag template.
847  *
848  * @return string
849  */
850 function get_tag_template() {
851         $tag = get_queried_object();
852
853         $templates = array();
854
855         $templates[] = "tag-{$tag->slug}.php";
856         $templates[] = "tag-{$tag->term_id}.php";
857         $templates[] = 'tag.php';
858
859         return get_query_template( 'tag', $templates );
860 }
861
862 /**
863  * Retrieve path of taxonomy template in current or parent template.
864  *
865  * Retrieves the taxonomy and term, if term is available. The template is
866  * prepended with 'taxonomy-' and followed by both the taxonomy string and
867  * the taxonomy string followed by a dash and then followed by the term.
868  *
869  * The taxonomy and term template is checked and used first, if it exists.
870  * Second, just the taxonomy template is checked, and then finally, taxonomy.php
871  * template is used. If none of the files exist, then it will fall back on to
872  * index.php.
873  *
874  * @since 2.5.0
875  * @uses apply_filters() Calls 'taxonomy_template' filter on found path.
876  *
877  * @return string
878  */
879 function get_taxonomy_template() {
880         $term = get_queried_object();
881         $taxonomy = $term->taxonomy;
882
883         $templates = array();
884
885         $templates[] = "taxonomy-$taxonomy-{$term->slug}.php";
886         $templates[] = "taxonomy-$taxonomy.php";
887         $templates[] = 'taxonomy.php';
888
889         return get_query_template( 'taxonomy', $templates );
890 }
891
892 /**
893  * Retrieve path of date template in current or parent template.
894  *
895  * @since 1.5.0
896  *
897  * @return string
898  */
899 function get_date_template() {
900         return get_query_template('date');
901 }
902
903 /**
904  * Retrieve path of home template in current or parent template.
905  *
906  * This is the template used for the page containing the blog posts
907  *
908  * Attempts to locate 'home.php' first before falling back to 'index.php'.
909  *
910  * @since 1.5.0
911  * @uses apply_filters() Calls 'home_template' on file path of home template.
912  *
913  * @return string
914  */
915 function get_home_template() {
916         $templates = array( 'home.php', 'index.php' );
917
918         return get_query_template( 'home', $templates );
919 }
920
921 /**
922  * Retrieve path of front-page template in current or parent template.
923  *
924  * Looks for 'front-page.php'.
925  *
926  * @since 3.0.0
927  * @uses apply_filters() Calls 'front_page_template' on file path of template.
928  *
929  * @return string
930  */
931 function get_front_page_template() {
932         $templates = array('front-page.php');
933
934         return get_query_template( 'front_page', $templates );
935 }
936
937 /**
938  * Retrieve path of page template in current or parent template.
939  *
940  * Will first look for the specifically assigned page template
941  * The will search for 'page-{slug}.php' followed by 'page-id.php'
942  * and finally 'page.php'
943  *
944  * @since 1.5.0
945  *
946  * @return string
947  */
948 function get_page_template() {
949         $id = get_queried_object_id();
950         $template = get_post_meta($id, '_wp_page_template', true);
951         $pagename = get_query_var('pagename');
952
953         if ( !$pagename && $id > 0 ) {
954                 // If a static page is set as the front page, $pagename will not be set. Retrieve it from the queried object
955                 $post = get_queried_object();
956                 $pagename = $post->post_name;
957         }
958
959         if ( 'default' == $template )
960                 $template = '';
961
962         $templates = array();
963         if ( !empty($template) && !validate_file($template) )
964                 $templates[] = $template;
965         if ( $pagename )
966                 $templates[] = "page-$pagename.php";
967         if ( $id )
968                 $templates[] = "page-$id.php";
969         $templates[] = 'page.php';
970
971         return get_query_template( 'page', $templates );
972 }
973
974 /**
975  * Retrieve path of paged template in current or parent template.
976  *
977  * @since 1.5.0
978  *
979  * @return string
980  */
981 function get_paged_template() {
982         return get_query_template('paged');
983 }
984
985 /**
986  * Retrieve path of search template in current or parent template.
987  *
988  * @since 1.5.0
989  *
990  * @return string
991  */
992 function get_search_template() {
993         return get_query_template('search');
994 }
995
996 /**
997  * Retrieve path of single template in current or parent template.
998  *
999  * @since 1.5.0
1000  *
1001  * @return string
1002  */
1003 function get_single_template() {
1004         $object = get_queried_object();
1005
1006         $templates = array();
1007
1008         $templates[] = "single-{$object->post_type}.php";
1009         $templates[] = "single.php";
1010
1011         return get_query_template( 'single', $templates );
1012 }
1013
1014 /**
1015  * Retrieve path of attachment template in current or parent template.
1016  *
1017  * The attachment path first checks if the first part of the mime type exists.
1018  * The second check is for the second part of the mime type. The last check is
1019  * for both types separated by an underscore. If neither are found then the file
1020  * 'attachment.php' is checked and returned.
1021  *
1022  * Some examples for the 'text/plain' mime type are 'text.php', 'plain.php', and
1023  * finally 'text_plain.php'.
1024  *
1025  * @since 2.0.0
1026  *
1027  * @return string
1028  */
1029 function get_attachment_template() {
1030         global $posts;
1031         $type = explode('/', $posts[0]->post_mime_type);
1032         if ( $template = get_query_template($type[0]) )
1033                 return $template;
1034         elseif ( $template = get_query_template($type[1]) )
1035                 return $template;
1036         elseif ( $template = get_query_template("$type[0]_$type[1]") )
1037                 return $template;
1038         else
1039                 return get_query_template('attachment');
1040 }
1041
1042 /**
1043  * Retrieve path of comment popup template in current or parent template.
1044  *
1045  * Checks for comment popup template in current template, if it exists or in the
1046  * parent template.
1047  *
1048  * @since 1.5.0
1049  * @uses apply_filters() Calls 'comments_popup_template' filter on path.
1050  *
1051  * @return string
1052  */
1053 function get_comments_popup_template() {
1054         $template = get_query_template( 'comments_popup', array( 'comments-popup.php' ) );
1055
1056         // Backward compat code will be removed in a future release
1057         if ('' == $template)
1058                 $template = ABSPATH . WPINC . '/theme-compat/comments-popup.php';
1059
1060         return $template;
1061 }
1062
1063 /**
1064  * Retrieve the name of the highest priority template file that exists.
1065  *
1066  * Searches in the STYLESHEETPATH before TEMPLATEPATH so that themes which
1067  * inherit from a parent theme can just overload one file.
1068  *
1069  * @since 2.7.0
1070  *
1071  * @param string|array $template_names Template file(s) to search for, in order.
1072  * @param bool $load If true the template file will be loaded if it is found.
1073  * @param bool $require_once Whether to require_once or require. Default true. Has no effect if $load is false.
1074  * @return string The template filename if one is located.
1075  */
1076 function locate_template($template_names, $load = false, $require_once = true ) {
1077         $located = '';
1078         foreach ( (array) $template_names as $template_name ) {
1079                 if ( !$template_name )
1080                         continue;
1081                 if ( file_exists(STYLESHEETPATH . '/' . $template_name)) {
1082                         $located = STYLESHEETPATH . '/' . $template_name;
1083                         break;
1084                 } else if ( file_exists(TEMPLATEPATH . '/' . $template_name) ) {
1085                         $located = TEMPLATEPATH . '/' . $template_name;
1086                         break;
1087                 }
1088         }
1089
1090         if ( $load && '' != $located )
1091                 load_template( $located, $require_once );
1092
1093         return $located;
1094 }
1095
1096 /**
1097  * Require the template file with WordPress environment.
1098  *
1099  * The globals are set up for the template file to ensure that the WordPress
1100  * environment is available from within the function. The query variables are
1101  * also available.
1102  *
1103  * @since 1.5.0
1104  *
1105  * @param string $_template_file Path to template file.
1106  * @param bool $require_once Whether to require_once or require. Default true.
1107  */
1108 function load_template( $_template_file, $require_once = true ) {
1109         global $posts, $post, $wp_did_header, $wp_did_template_redirect, $wp_query, $wp_rewrite, $wpdb, $wp_version, $wp, $id, $comment, $user_ID;
1110
1111         if ( is_array( $wp_query->query_vars ) )
1112                 extract( $wp_query->query_vars, EXTR_SKIP );
1113
1114         if ( $require_once )
1115                 require_once( $_template_file );
1116         else
1117                 require( $_template_file );
1118 }
1119
1120 /**
1121  * Display localized stylesheet link element.
1122  *
1123  * @since 2.1.0
1124  */
1125 function locale_stylesheet() {
1126         $stylesheet = get_locale_stylesheet_uri();
1127         if ( empty($stylesheet) )
1128                 return;
1129         echo '<link rel="stylesheet" href="' . $stylesheet . '" type="text/css" media="screen" />';
1130 }
1131
1132 /**
1133  * Start preview theme output buffer.
1134  *
1135  * Will only preform task if the user has permissions and template and preview
1136  * query variables exist.
1137  *
1138  * @since 2.6.0
1139  */
1140 function preview_theme() {
1141         if ( ! (isset($_GET['template']) && isset($_GET['preview'])) )
1142                 return;
1143
1144         if ( !current_user_can( 'switch_themes' ) )
1145                 return;
1146
1147         // Admin Thickbox requests
1148         if ( isset( $_GET['preview_iframe'] ) )
1149                 show_admin_bar( false );
1150
1151         $_GET['template'] = preg_replace('|[^a-z0-9_./-]|i', '', $_GET['template']);
1152
1153         if ( validate_file($_GET['template']) )
1154                 return;
1155
1156         add_filter( 'template', '_preview_theme_template_filter' );
1157
1158         if ( isset($_GET['stylesheet']) ) {
1159                 $_GET['stylesheet'] = preg_replace('|[^a-z0-9_./-]|i', '', $_GET['stylesheet']);
1160                 if ( validate_file($_GET['stylesheet']) )
1161                         return;
1162                 add_filter( 'stylesheet', '_preview_theme_stylesheet_filter' );
1163         }
1164
1165         // Prevent theme mods to current theme being used on theme being previewed
1166         add_filter( 'pre_option_mods_' . get_current_theme(), '__return_empty_array' );
1167
1168         ob_start( 'preview_theme_ob_filter' );
1169 }
1170 add_action('setup_theme', 'preview_theme');
1171
1172 /**
1173  * Private function to modify the current template when previewing a theme
1174  *
1175  * @since 2.9.0
1176  * @access private
1177  *
1178  * @return string
1179  */
1180 function _preview_theme_template_filter() {
1181         return isset($_GET['template']) ? $_GET['template'] : '';
1182 }
1183
1184 /**
1185  * Private function to modify the current stylesheet when previewing a theme
1186  *
1187  * @since 2.9.0
1188  * @access private
1189  *
1190  * @return string
1191  */
1192 function _preview_theme_stylesheet_filter() {
1193         return isset($_GET['stylesheet']) ? $_GET['stylesheet'] : '';
1194 }
1195
1196 /**
1197  * Callback function for ob_start() to capture all links in the theme.
1198  *
1199  * @since 2.6.0
1200  * @access private
1201  *
1202  * @param string $content
1203  * @return string
1204  */
1205 function preview_theme_ob_filter( $content ) {
1206         return preg_replace_callback( "|(<a.*?href=([\"']))(.*?)([\"'].*?>)|", 'preview_theme_ob_filter_callback', $content );
1207 }
1208
1209 /**
1210  * Manipulates preview theme links in order to control and maintain location.
1211  *
1212  * Callback function for preg_replace_callback() to accept and filter matches.
1213  *
1214  * @since 2.6.0
1215  * @access private
1216  *
1217  * @param array $matches
1218  * @return string
1219  */
1220 function preview_theme_ob_filter_callback( $matches ) {
1221         if ( strpos($matches[4], 'onclick') !== false )
1222                 $matches[4] = preg_replace('#onclick=([\'"]).*?(?<!\\\)\\1#i', '', $matches[4]); //Strip out any onclicks from rest of <a>. (?<!\\\) means to ignore the '" if its escaped by \  to prevent breaking mid-attribute.
1223         if (
1224                 ( false !== strpos($matches[3], '/wp-admin/') )
1225         ||
1226                 ( false !== strpos( $matches[3], '://' ) && 0 !== strpos( $matches[3], home_url() ) )
1227         ||
1228                 ( false !== strpos($matches[3], '/feed/') )
1229         ||
1230                 ( false !== strpos($matches[3], '/trackback/') )
1231         )
1232                 return $matches[1] . "#$matches[2] onclick=$matches[2]return false;" . $matches[4];
1233
1234         $link = add_query_arg( array('preview' => 1, 'template' => $_GET['template'], 'stylesheet' => @$_GET['stylesheet'] ), $matches[3] );
1235         if ( 0 === strpos($link, 'preview=1') )
1236                 $link = "?$link";
1237         return $matches[1] . esc_attr( $link ) . $matches[4];
1238 }
1239
1240 /**
1241  * Switches current theme to new template and stylesheet names.
1242  *
1243  * @since 2.5.0
1244  * @uses do_action() Calls 'switch_theme' action on updated theme display name.
1245  *
1246  * @param string $template Template name
1247  * @param string $stylesheet Stylesheet name.
1248  */
1249 function switch_theme($template, $stylesheet) {
1250         global $wp_theme_directories, $sidebars_widgets;
1251
1252         if ( is_array( $sidebars_widgets ) )
1253                 set_theme_mod( 'sidebars_widgets', array( 'time' => time(), 'data' => $sidebars_widgets ) );
1254
1255         $old_theme = get_current_theme();
1256
1257         update_option('template', $template);
1258         update_option('stylesheet', $stylesheet);
1259
1260         if ( count($wp_theme_directories) > 1 ) {
1261                 update_option('template_root', get_raw_theme_root($template, true));
1262                 update_option('stylesheet_root', get_raw_theme_root($stylesheet, true));
1263         }
1264
1265         delete_option('current_theme');
1266         $theme = get_current_theme();
1267
1268         if ( is_admin() && false === get_option( "theme_mods_$stylesheet" ) ) {
1269                 $default_theme_mods = (array) get_option( "mods_$theme" );
1270                 add_option( "theme_mods_$stylesheet", $default_theme_mods );
1271         }
1272
1273         update_option( 'theme_switched', $old_theme );
1274         do_action( 'switch_theme', $theme );
1275 }
1276
1277 /**
1278  * Checks that current theme files 'index.php' and 'style.css' exists.
1279  *
1280  * Does not check the default theme, which is the fallback and should always exist.
1281  * Will switch theme to the fallback theme if current theme does not validate.
1282  * You can use the 'validate_current_theme' filter to return FALSE to
1283  * disable this functionality.
1284  *
1285  * @since 1.5.0
1286  * @see WP_DEFAULT_THEME
1287  *
1288  * @return bool
1289  */
1290 function validate_current_theme() {
1291         // Don't validate during an install/upgrade.
1292         if ( defined('WP_INSTALLING') || !apply_filters( 'validate_current_theme', true ) )
1293                 return true;
1294
1295         if ( get_template() != WP_DEFAULT_THEME && !file_exists(get_template_directory() . '/index.php') ) {
1296                 switch_theme( WP_DEFAULT_THEME, WP_DEFAULT_THEME );
1297                 return false;
1298         }
1299
1300         if ( get_stylesheet() != WP_DEFAULT_THEME && !file_exists(get_template_directory() . '/style.css') ) {
1301                 switch_theme( WP_DEFAULT_THEME, WP_DEFAULT_THEME );
1302                 return false;
1303         }
1304
1305         if ( is_child_theme() && ! file_exists( get_stylesheet_directory() . '/style.css' ) ) {
1306                 switch_theme( WP_DEFAULT_THEME, WP_DEFAULT_THEME );
1307                 return false;
1308         }
1309
1310         return true;
1311 }
1312
1313 /**
1314  * Retrieve all theme modifications.
1315  *
1316  * @since 3.1.0
1317  *
1318  * @return array Theme modifications.
1319  */
1320 function get_theme_mods() {
1321         $theme_slug = get_option( 'stylesheet' );
1322         if ( false === ( $mods = get_option( "theme_mods_$theme_slug" ) ) ) {
1323                 $theme_name = get_current_theme();
1324                 $mods = get_option( "mods_$theme_name" ); // Deprecated location.
1325                 if ( is_admin() && false !== $mods ) {
1326                         update_option( "theme_mods_$theme_slug", $mods );
1327                         delete_option( "mods_$theme_name" );
1328                 }
1329         }
1330         return $mods;
1331 }
1332
1333 /**
1334  * Retrieve theme modification value for the current theme.
1335  *
1336  * If the modification name does not exist, then the $default will be passed
1337  * through {@link http://php.net/sprintf sprintf()} PHP function with the first
1338  * string the template directory URI and the second string the stylesheet
1339  * directory URI.
1340  *
1341  * @since 2.1.0
1342  * @uses apply_filters() Calls 'theme_mod_$name' filter on the value.
1343  *
1344  * @param string $name Theme modification name.
1345  * @param bool|string $default
1346  * @return string
1347  */
1348 function get_theme_mod( $name, $default = false ) {
1349         $mods = get_theme_mods();
1350
1351         if ( isset( $mods[ $name ] ) )
1352                 return apply_filters( "theme_mod_$name", $mods[ $name ] );
1353
1354         return apply_filters( "theme_mod_$name", sprintf( $default, get_template_directory_uri(), get_stylesheet_directory_uri() ) );
1355 }
1356
1357 /**
1358  * Update theme modification value for the current theme.
1359  *
1360  * @since 2.1.0
1361  *
1362  * @param string $name Theme modification name.
1363  * @param string $value theme modification value.
1364  */
1365 function set_theme_mod( $name, $value ) {
1366         $mods = get_theme_mods();
1367
1368         $mods[ $name ] = $value;
1369
1370         $theme = get_option( 'stylesheet' );
1371         update_option( "theme_mods_$theme", $mods );
1372 }
1373
1374 /**
1375  * Remove theme modification name from current theme list.
1376  *
1377  * If removing the name also removes all elements, then the entire option will
1378  * be removed.
1379  *
1380  * @since 2.1.0
1381  *
1382  * @param string $name Theme modification name.
1383  * @return null
1384  */
1385 function remove_theme_mod( $name ) {
1386         $mods = get_theme_mods();
1387
1388         if ( ! isset( $mods[ $name ] ) )
1389                 return;
1390
1391         unset( $mods[ $name ] );
1392
1393         if ( empty( $mods ) )
1394                 return remove_theme_mods();
1395
1396         $theme = get_option( 'stylesheet' );
1397         update_option( "theme_mods_$theme", $mods );
1398 }
1399
1400 /**
1401  * Remove theme modifications option for current theme.
1402  *
1403  * @since 2.1.0
1404  */
1405 function remove_theme_mods() {
1406         delete_option( 'theme_mods_' . get_option( 'stylesheet' ) );
1407         delete_option( 'mods_' . get_current_theme() );
1408 }
1409
1410 /**
1411  * Retrieve text color for custom header.
1412  *
1413  * @since 2.1.0
1414  * @uses HEADER_TEXTCOLOR
1415  *
1416  * @return string
1417  */
1418 function get_header_textcolor() {
1419         $default = defined('HEADER_TEXTCOLOR') ? HEADER_TEXTCOLOR : '';
1420
1421         return get_theme_mod('header_textcolor', $default);
1422 }
1423
1424 /**
1425  * Display text color for custom header.
1426  *
1427  * @since 2.1.0
1428  */
1429 function header_textcolor() {
1430         echo get_header_textcolor();
1431 }
1432
1433 /**
1434  * Retrieve header image for custom header.
1435  *
1436  * @since 2.1.0
1437  * @uses HEADER_IMAGE
1438  *
1439  * @return string
1440  */
1441 function get_header_image() {
1442         $default = defined( 'HEADER_IMAGE' ) ? HEADER_IMAGE : '';
1443         $url = get_theme_mod( 'header_image', $default );
1444
1445         if ( 'remove-header' == $url )
1446                 return false;
1447
1448         if ( is_random_header_image() )
1449                 $url = get_random_header_image();
1450
1451         if ( is_ssl() )
1452                 $url = str_replace( 'http://', 'https://', $url );
1453         else
1454                 $url = str_replace( 'https://', 'http://', $url );
1455
1456         return esc_url_raw( $url );
1457 }
1458
1459 /**
1460  * Get random header image from registered images in theme.
1461  *
1462  * @since 3.2.0
1463  *
1464  * @return string Path to header image
1465  */
1466 function get_random_header_image() {
1467         global $_wp_default_headers;
1468
1469         $header_image_mod = get_theme_mod( 'header_image', '' );
1470         $headers = array();
1471
1472         if ( 'random-uploaded-image' == $header_image_mod )
1473                 $headers = get_uploaded_header_images();
1474         elseif ( ! empty( $_wp_default_headers ) ) {
1475                 if ( 'random-default-image' == $header_image_mod ) {
1476                         $headers = $_wp_default_headers;
1477                 } else {
1478                         $is_random = get_theme_support( 'custom-header' );
1479                         if ( isset( $is_random[ 0 ] ) && !empty( $is_random[ 0 ][ 'random-default' ] ) )
1480                                 $headers = $_wp_default_headers;
1481                 }
1482         }
1483
1484         if ( empty( $headers ) )
1485                 return '';
1486
1487         $random_image = array_rand( $headers );
1488         $header_url = sprintf( $headers[$random_image]['url'], get_template_directory_uri(), get_stylesheet_directory_uri() );
1489
1490         return $header_url;
1491 }
1492
1493 /**
1494  * Check if random header image is in use.
1495  *
1496  * Always true if user expressly chooses the option in Appearance > Header.
1497  * Also true if theme has multiple header images registered, no specific header image
1498  * is chosen, and theme turns on random headers with add_theme_support().
1499  *
1500  * @since 3.2.0
1501  * @uses HEADER_IMAGE
1502  *
1503  * @param string $type The random pool to use. any|default|uploaded
1504  * @return boolean
1505  */
1506 function is_random_header_image( $type = 'any' ) {
1507         $default = defined( 'HEADER_IMAGE' ) ? HEADER_IMAGE : '';
1508         $header_image_mod = get_theme_mod( 'header_image', $default );
1509
1510         if ( 'any' == $type ) {
1511                 if ( 'random-default-image' == $header_image_mod || 'random-uploaded-image' == $header_image_mod || ( '' != get_random_header_image() && empty( $header_image_mod ) ) )
1512                         return true;
1513         } else {
1514                 if ( "random-$type-image" == $header_image_mod )
1515                         return true;
1516                 elseif ( 'default' == $type && empty( $header_image_mod ) && '' != get_random_header_image() )
1517                         return true;
1518         }
1519
1520         return false;
1521 }
1522
1523 /**
1524  * Display header image path.
1525  *
1526  * @since 2.1.0
1527  */
1528 function header_image() {
1529         echo get_header_image();
1530 }
1531
1532 /**
1533  * Get the header images uploaded for the current theme.
1534  *
1535  * @since 3.2.0
1536  *
1537  * @return array
1538  */
1539 function get_uploaded_header_images() {
1540         $header_images = array();
1541
1542         // @todo caching
1543         $headers = get_posts( array( 'post_type' => 'attachment', 'meta_key' => '_wp_attachment_is_custom_header', 'meta_value' => get_option('stylesheet'), 'orderby' => 'none', 'nopaging' => true ) );
1544
1545         if ( empty( $headers ) )
1546                 return array();
1547
1548         foreach ( (array) $headers as $header ) {
1549                 $url = esc_url_raw( $header->guid );
1550                 $header = basename($url);
1551                 $header_images[$header] = array();
1552                 $header_images[$header]['url'] =  $url;
1553                 $header_images[$header]['thumbnail_url'] =  $url;
1554                 $header_images[$header]['uploaded'] = true;
1555         }
1556
1557         return $header_images;
1558 }
1559
1560 /**
1561  * Add callbacks for image header display.
1562  *
1563  * The parameter $header_callback callback will be required to display the
1564  * content for the 'wp_head' action. The parameter $admin_header_callback
1565  * callback will be added to Custom_Image_Header class and that will be added
1566  * to the 'admin_menu' action.
1567  *
1568  * @since 2.1.0
1569  * @uses Custom_Image_Header Sets up for $admin_header_callback for administration panel display.
1570  *
1571  * @param callback $header_callback Call on 'wp_head' action.
1572  * @param callback $admin_header_callback Call on custom header administration screen.
1573  * @param callback $admin_image_div_callback Output a custom header image div on the custom header administration screen. Optional.
1574  */
1575 function add_custom_image_header( $header_callback, $admin_header_callback, $admin_image_div_callback = '' ) {
1576         if ( ! empty( $header_callback ) )
1577                 add_action('wp_head', $header_callback);
1578
1579         $support = array( 'callback' => $header_callback );
1580         $theme_support = get_theme_support( 'custom-header' );
1581         if ( ! empty( $theme_support ) && is_array( $theme_support[ 0 ] ) )
1582                 $support = array_merge( $theme_support[ 0 ], $support );
1583         add_theme_support( 'custom-header',  $support );
1584         add_theme_support( 'custom-header-uploads' );
1585
1586         if ( ! is_admin() )
1587                 return;
1588
1589         global $custom_image_header;
1590
1591         require_once( ABSPATH . 'wp-admin/custom-header.php' );
1592         $custom_image_header = new Custom_Image_Header( $admin_header_callback, $admin_image_div_callback );
1593         add_action( 'admin_menu', array( &$custom_image_header, 'init' ) );
1594 }
1595
1596 /**
1597  * Remove image header support.
1598  *
1599  * @since 3.1.0
1600  * @see add_custom_image_header()
1601  *
1602  * @return bool Whether support was removed.
1603  */
1604 function remove_custom_image_header() {
1605         if ( ! current_theme_supports( 'custom-header' ) )
1606                 return false;
1607
1608         $callback = get_theme_support( 'custom-header' );
1609         remove_action( 'wp_head', $callback[0]['callback'] );
1610         _remove_theme_support( 'custom-header' );
1611         remove_theme_support( 'custom-header-uploads' );
1612
1613         if ( is_admin() ) {
1614                 remove_action( 'admin_menu', array( &$GLOBALS['custom_image_header'], 'init' ) );
1615                 unset( $GLOBALS['custom_image_header'] );
1616         }
1617
1618         return true;
1619 }
1620
1621 /**
1622  * Register a selection of default headers to be displayed by the custom header admin UI.
1623  *
1624  * @since 3.0.0
1625  *
1626  * @param array $headers Array of headers keyed by a string id. The ids point to arrays containing 'url', 'thumbnail_url', and 'description' keys.
1627  */
1628 function register_default_headers( $headers ) {
1629         global $_wp_default_headers;
1630
1631         $_wp_default_headers = array_merge( (array) $_wp_default_headers, (array) $headers );
1632 }
1633
1634 /**
1635  * Unregister default headers.
1636  *
1637  * This function must be called after register_default_headers() has already added the
1638  * header you want to remove.
1639  *
1640  * @see register_default_headers()
1641  * @since 3.0.0
1642  *
1643  * @param string|array $header The header string id (key of array) to remove, or an array thereof.
1644  * @return True on success, false on failure.
1645  */
1646 function unregister_default_headers( $header ) {
1647         global $_wp_default_headers;
1648         if ( is_array( $header ) ) {
1649                 array_map( 'unregister_default_headers', $header );
1650         } elseif ( isset( $_wp_default_headers[ $header ] ) ) {
1651                 unset( $_wp_default_headers[ $header ] );
1652                 return true;
1653         } else {
1654                 return false;
1655         }
1656 }
1657
1658 /**
1659  * Retrieve background image for custom background.
1660  *
1661  * @since 3.0.0
1662  *
1663  * @return string
1664  */
1665 function get_background_image() {
1666         $default = defined('BACKGROUND_IMAGE') ? BACKGROUND_IMAGE : '';
1667
1668         return get_theme_mod('background_image', $default);
1669 }
1670
1671 /**
1672  * Display background image path.
1673  *
1674  * @since 3.0.0
1675  */
1676 function background_image() {
1677         echo get_background_image();
1678 }
1679
1680 /**
1681  * Retrieve value for custom background color.
1682  *
1683  * @since 3.0.0
1684  * @uses BACKGROUND_COLOR
1685  *
1686  * @return string
1687  */
1688 function get_background_color() {
1689         $default = defined('BACKGROUND_COLOR') ? BACKGROUND_COLOR : '';
1690
1691         return get_theme_mod('background_color', $default);
1692 }
1693
1694 /**
1695  * Display background color value.
1696  *
1697  * @since 3.0.0
1698  */
1699 function background_color() {
1700         echo get_background_color();
1701 }
1702
1703 /**
1704  * Add callbacks for background image display.
1705  *
1706  * The parameter $header_callback callback will be required to display the
1707  * content for the 'wp_head' action. The parameter $admin_header_callback
1708  * callback will be added to Custom_Background class and that will be added
1709  * to the 'admin_menu' action.
1710  *
1711  * @since 3.0.0
1712  * @uses Custom_Background Sets up for $admin_header_callback for administration panel display.
1713  *
1714  * @param callback $header_callback Call on 'wp_head' action.
1715  * @param callback $admin_header_callback Call on custom background administration screen.
1716  * @param callback $admin_image_div_callback Output a custom background image div on the custom background administration screen. Optional.
1717  */
1718 function add_custom_background( $header_callback = '', $admin_header_callback = '', $admin_image_div_callback = '' ) {
1719         if ( isset( $GLOBALS['custom_background'] ) )
1720                 return;
1721
1722         if ( empty( $header_callback ) )
1723                 $header_callback = '_custom_background_cb';
1724
1725         add_action( 'wp_head', $header_callback );
1726
1727         add_theme_support( 'custom-background', array( 'callback' => $header_callback ) );
1728
1729         if ( ! is_admin() )
1730                 return;
1731         require_once( ABSPATH . 'wp-admin/custom-background.php' );
1732         $GLOBALS['custom_background'] = new Custom_Background( $admin_header_callback, $admin_image_div_callback );
1733         add_action( 'admin_menu', array( &$GLOBALS['custom_background'], 'init' ) );
1734 }
1735
1736 /**
1737  * Remove custom background support.
1738  *
1739  * @since 3.1.0
1740  * @see add_custom_background()
1741  *
1742  * @return bool Whether support was removed.
1743  */
1744 function remove_custom_background() {
1745         if ( ! current_theme_supports( 'custom-background' ) )
1746                 return false;
1747
1748         $callback = get_theme_support( 'custom-background' );
1749         remove_action( 'wp_head', $callback[0]['callback'] );
1750         _remove_theme_support( 'custom-background' );
1751
1752         if ( is_admin() ) {
1753                 remove_action( 'admin_menu', array( &$GLOBALS['custom_background'], 'init' ) );
1754                 unset( $GLOBALS['custom_background'] );
1755         }
1756
1757         return true;
1758 }
1759
1760 /**
1761  * Default custom background callback.
1762  *
1763  * @since 3.0.0
1764  * @see add_custom_background()
1765  * @access protected
1766  */
1767 function _custom_background_cb() {
1768         $background = get_background_image();
1769         $color = get_background_color();
1770         if ( ! $background && ! $color )
1771                 return;
1772
1773         $style = $color ? "background-color: #$color;" : '';
1774
1775         if ( $background ) {
1776                 $image = " background-image: url('$background');";
1777
1778                 $repeat = get_theme_mod( 'background_repeat', 'repeat' );
1779                 if ( ! in_array( $repeat, array( 'no-repeat', 'repeat-x', 'repeat-y', 'repeat' ) ) )
1780                         $repeat = 'repeat';
1781                 $repeat = " background-repeat: $repeat;";
1782
1783                 $position = get_theme_mod( 'background_position_x', 'left' );
1784                 if ( ! in_array( $position, array( 'center', 'right', 'left' ) ) )
1785                         $position = 'left';
1786                 $position = " background-position: top $position;";
1787
1788                 $attachment = get_theme_mod( 'background_attachment', 'scroll' );
1789                 if ( ! in_array( $attachment, array( 'fixed', 'scroll' ) ) )
1790                         $attachment = 'scroll';
1791                 $attachment = " background-attachment: $attachment;";
1792
1793                 $style .= $image . $repeat . $position . $attachment;
1794         }
1795 ?>
1796 <style type="text/css">
1797 body.custom-background { <?php echo trim( $style ); ?> }
1798 </style>
1799 <?php
1800 }
1801
1802 /**
1803  * Add callback for custom TinyMCE editor stylesheets.
1804  *
1805  * The parameter $stylesheet is the name of the stylesheet, relative to
1806  * the theme root. It also accepts an array of stylesheets.
1807  * It is optional and defaults to 'editor-style.css'.
1808  *
1809  * Supports RTL stylesheets automatically by searching for the -rtl prefix, e.g.
1810  * editor-style-rtl.css. If an array of stylesheets is passed to add_editor_style(),
1811  * RTL is only added for the first stylesheet.
1812  *
1813  * @since 3.0.0
1814  *
1815  * @param mixed $stylesheet Optional. Stylesheet name or array thereof, relative to theme root.
1816  *      Defaults to 'editor-style.css'
1817  */
1818 function add_editor_style( $stylesheet = 'editor-style.css' ) {
1819
1820         add_theme_support( 'editor-style' );
1821
1822         if ( ! is_admin() )
1823                 return;
1824
1825         global $editor_styles;
1826         $editor_styles = (array) $editor_styles;
1827         $stylesheet    = (array) $stylesheet;
1828         if ( is_rtl() ) {
1829                 $rtl_stylesheet = str_replace('.css', '-rtl.css', $stylesheet[0]);
1830                 $stylesheet[] = $rtl_stylesheet;
1831         }
1832
1833         $editor_styles = array_merge( $editor_styles, $stylesheet );
1834 }
1835
1836 /**
1837  * Removes all visual editor stylesheets.
1838  *
1839  * @since 3.1.0
1840  *
1841  * @return bool True on success, false if there were no stylesheets to remove.
1842  */
1843 function remove_editor_styles() {
1844         if ( ! current_theme_supports( 'editor-style' ) )
1845                 return false;
1846         _remove_theme_support( 'editor-style' );
1847         if ( is_admin() )
1848                 $GLOBALS['editor_styles'] = array();
1849         return true;
1850 }
1851
1852 /**
1853  * Allows a theme to register its support of a certain feature
1854  *
1855  * Must be called in the theme's functions.php file to work.
1856  * If attached to a hook, it must be after_setup_theme.
1857  * The init hook may be too late for some features.
1858  *
1859  * @since 2.9.0
1860  * @param string $feature the feature being added
1861  */
1862 function add_theme_support( $feature ) {
1863         global $_wp_theme_features;
1864
1865         if ( func_num_args() == 1 )
1866                 $_wp_theme_features[$feature] = true;
1867         else
1868                 $_wp_theme_features[$feature] = array_slice( func_get_args(), 1 );
1869
1870         if ( $feature == 'post-formats' && is_array( $_wp_theme_features[$feature][0] ) )
1871                 $_wp_theme_features[$feature][0] = array_intersect( $_wp_theme_features[$feature][0], array_keys( get_post_format_slugs() ) );
1872 }
1873
1874 /**
1875  * Gets the theme support arguments passed when registering that support
1876  *
1877  * @since 3.1
1878  * @param string $feature the feature to check
1879  * @return array The array of extra arguments
1880  */
1881 function get_theme_support( $feature ) {
1882         global $_wp_theme_features;
1883         if ( !isset( $_wp_theme_features[$feature] ) )
1884                 return false;
1885         else
1886                 return $_wp_theme_features[$feature];
1887 }
1888
1889 /**
1890  * Allows a theme to de-register its support of a certain feature
1891  *
1892  * Should be called in the theme's functions.php file. Generally would
1893  * be used for child themes to override support from the parent theme.
1894  *
1895  * @since 3.0.0
1896  * @see add_theme_support()
1897  * @param string $feature the feature being added
1898  * @return bool Whether feature was removed.
1899  */
1900 function remove_theme_support( $feature ) {
1901         // Blacklist: for internal registrations not used directly by themes.
1902         if ( in_array( $feature, array( 'custom-background', 'custom-header', 'editor-style', 'widgets', 'menus' ) ) )
1903                 return false;
1904         return _remove_theme_support( $feature );
1905 }
1906
1907 /**
1908  * Do not use. Removes theme support internally, ignorant of the blacklist.
1909  *
1910  * @access private
1911  * @since 3.1.0
1912  */
1913 function _remove_theme_support( $feature ) {
1914         global $_wp_theme_features;
1915
1916         if ( ! isset( $_wp_theme_features[$feature] ) )
1917                 return false;
1918         unset( $_wp_theme_features[$feature] );
1919         return true;
1920 }
1921
1922 /**
1923  * Checks a theme's support for a given feature
1924  *
1925  * @since 2.9.0
1926  * @param string $feature the feature being checked
1927  * @return boolean
1928  */
1929 function current_theme_supports( $feature ) {
1930         global $_wp_theme_features;
1931
1932         if ( !isset( $_wp_theme_features[$feature] ) )
1933                 return false;
1934
1935         // If no args passed then no extra checks need be performed
1936         if ( func_num_args() <= 1 )
1937                 return true;
1938
1939         $args = array_slice( func_get_args(), 1 );
1940
1941         // @todo Allow pluggable arg checking
1942         switch ( $feature ) {
1943                 case 'post-thumbnails':
1944                         // post-thumbnails can be registered for only certain content/post types by passing
1945                         // an array of types to add_theme_support().  If no array was passed, then
1946                         // any type is accepted
1947                         if ( true === $_wp_theme_features[$feature] )  // Registered for all types
1948                                 return true;
1949                         $content_type = $args[0];
1950                         return in_array( $content_type, $_wp_theme_features[$feature][0] );
1951                         break;
1952
1953                 case 'post-formats':
1954                         // specific post formats can be registered by passing an array of types to
1955                         // add_theme_support()
1956                         $post_format = $args[0];
1957                         return in_array( $post_format, $_wp_theme_features[$feature][0] );
1958                         break;
1959         }
1960
1961         return true;
1962 }
1963
1964 /**
1965  * Checks a theme's support for a given feature before loading the functions which implement it.
1966  *
1967  * @since 2.9.0
1968  * @param string $feature the feature being checked
1969  * @param string $include the file containing the functions that implement the feature
1970  */
1971 function require_if_theme_supports( $feature, $include) {
1972         if ( current_theme_supports( $feature ) )
1973                 require ( $include );
1974 }
1975
1976 /**
1977  * Checks an attachment being deleted to see if it's a header or background image.
1978  *
1979  * If true it removes the theme modification which would be pointing at the deleted
1980  * attachment
1981  *
1982  * @access private
1983  * @since 3.0.0
1984  * @param int $id the attachment id
1985  */
1986 function _delete_attachment_theme_mod( $id ) {
1987         $attachment_image = wp_get_attachment_url( $id );
1988         $header_image = get_header_image();
1989         $background_image = get_background_image();
1990
1991         if ( $header_image && $header_image == $attachment_image )
1992                 remove_theme_mod( 'header_image' );
1993
1994         if ( $background_image && $background_image == $attachment_image )
1995                 remove_theme_mod( 'background_image' );
1996 }
1997
1998 add_action( 'delete_attachment', '_delete_attachment_theme_mod' );
1999
2000 /**
2001  * Checks if a theme has been changed and runs 'after_switch_theme' hook on the next WP load
2002  *
2003  * @since 3.3
2004  */
2005 function check_theme_switched() {
2006         if ( false !== ( $old_theme = get_option( 'theme_switched' ) ) && !empty( $old_theme ) ) {
2007                 do_action( 'after_switch_theme', $old_theme );
2008                 update_option( 'theme_switched', false );
2009         }
2010 }