Wordpress 3.2
[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 arry 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 ( 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         $theme_names = array_keys($themes);
522         $current_template = get_option('template');
523         $current_stylesheet = get_option('stylesheet');
524         $current_theme = 'Twenty Ten';
525
526         if ( $themes ) {
527                 foreach ( (array) $theme_names as $theme_name ) {
528                         if ( $themes[$theme_name]['Stylesheet'] == $current_stylesheet &&
529                                         $themes[$theme_name]['Template'] == $current_template ) {
530                                 $current_theme = $themes[$theme_name]['Name'];
531                                 break;
532                         }
533                 }
534         }
535
536         update_option('current_theme', $current_theme);
537
538         return $current_theme;
539 }
540
541 /**
542  * Register a directory that contains themes.
543  *
544  * @since 2.9.0
545  *
546  * @param string $directory Either the full filesystem path to a theme folder or a folder within WP_CONTENT_DIR
547  * @return bool
548  */
549 function register_theme_directory( $directory) {
550         global $wp_theme_directories;
551
552         /* If this folder does not exist, return and do not register */
553         if ( !file_exists( $directory ) )
554                         /* Try prepending as the theme directory could be relative to the content directory */
555                 $registered_directory = WP_CONTENT_DIR . '/' . $directory;
556         else
557                 $registered_directory = $directory;
558
559         /* If this folder does not exist, return and do not register */
560         if ( !file_exists( $registered_directory ) )
561                 return false;
562
563         $wp_theme_directories[] = $registered_directory;
564
565         return true;
566 }
567
568 /**
569  * Search all registered theme directories for complete and valid themes.
570  *
571  * @since 2.9.0
572  *
573  * @return array Valid themes found
574  */
575 function search_theme_directories() {
576         global $wp_theme_directories, $wp_broken_themes;
577         if ( empty( $wp_theme_directories ) )
578                 return false;
579
580         $theme_files = array();
581         $wp_broken_themes = array();
582
583         /* Loop the registered theme directories and extract all themes */
584         foreach ( (array) $wp_theme_directories as $theme_root ) {
585                 $theme_loc = $theme_root;
586
587                 /* We don't want to replace all forward slashes, see Trac #4541 */
588                 if ( '/' != WP_CONTENT_DIR )
589                         $theme_loc = str_replace(WP_CONTENT_DIR, '', $theme_root);
590
591                 /* Files in the root of the current theme directory and one subdir down */
592                 $themes_dir = @ opendir($theme_root);
593
594                 if ( !$themes_dir )
595                         return false;
596
597                 while ( ($theme_dir = readdir($themes_dir)) !== false ) {
598                         if ( is_dir($theme_root . '/' . $theme_dir) && is_readable($theme_root . '/' . $theme_dir) ) {
599                                 if ( $theme_dir[0] == '.' || $theme_dir == 'CVS' )
600                                         continue;
601
602                                 $stylish_dir = @opendir($theme_root . '/' . $theme_dir);
603                                 $found_stylesheet = false;
604
605                                 while ( ($theme_file = readdir($stylish_dir)) !== false ) {
606                                         if ( $theme_file == 'style.css' ) {
607                                                 $theme_files[$theme_dir] = array( 'theme_file' => $theme_dir . '/' . $theme_file, 'theme_root' => $theme_root );
608                                                 $found_stylesheet = true;
609                                                 break;
610                                         }
611                                 }
612                                 @closedir($stylish_dir);
613
614                                 if ( !$found_stylesheet ) { // look for themes in that dir
615                                         $subdir = "$theme_root/$theme_dir";
616                                         $subdir_name = $theme_dir;
617                                         $theme_subdirs = @opendir( $subdir );
618
619                                         $found_subdir_themes = false;
620                                         while ( ($theme_subdir = readdir($theme_subdirs)) !== false ) {
621                                                 if ( is_dir( $subdir . '/' . $theme_subdir) && is_readable($subdir . '/' . $theme_subdir) ) {
622                                                         if ( $theme_subdir[0] == '.' || $theme_subdir == 'CVS' )
623                                                                 continue;
624
625                                                         $stylish_dir = @opendir($subdir . '/' . $theme_subdir);
626                                                         $found_stylesheet = false;
627
628                                                         while ( ($theme_file = readdir($stylish_dir)) !== false ) {
629                                                                 if ( $theme_file == 'style.css' ) {
630                                                                         $theme_files["$theme_dir/$theme_subdir"] = array( 'theme_file' => $subdir_name . '/' . $theme_subdir . '/' . $theme_file, 'theme_root' => $theme_root );
631                                                                         $found_stylesheet = true;
632                                                                         $found_subdir_themes = true;
633                                                                         break;
634                                                                 }
635                                                         }
636                                                         @closedir($stylish_dir);
637                                                 }
638                                         }
639                                         @closedir($theme_subdirs);
640                                         if ( !$found_subdir_themes )
641                                                 $wp_broken_themes[$theme_dir] = array('Name' => $theme_dir, 'Title' => $theme_dir, 'Description' => __('Stylesheet is missing.'));
642                                 }
643                         }
644                 }
645                 @closedir( $themes_dir );
646         }
647         return $theme_files;
648 }
649
650 /**
651  * Retrieve path to themes directory.
652  *
653  * Does not have trailing slash.
654  *
655  * @since 1.5.0
656  * @uses apply_filters() Calls 'theme_root' filter on path.
657  *
658  * @param string $stylesheet_or_template The stylesheet or template name of the theme
659  * @return string Theme path.
660  */
661 function get_theme_root( $stylesheet_or_template = false ) {
662         if ( $stylesheet_or_template ) {
663                 if ( $theme_root = get_raw_theme_root($stylesheet_or_template) )
664                         $theme_root = WP_CONTENT_DIR . $theme_root;
665                 else
666                         $theme_root = WP_CONTENT_DIR . '/themes';
667         } else {
668                 $theme_root = WP_CONTENT_DIR . '/themes';
669         }
670
671         return apply_filters( 'theme_root', $theme_root );
672 }
673
674 /**
675  * Retrieve URI for themes directory.
676  *
677  * Does not have trailing slash.
678  *
679  * @since 1.5.0
680  *
681  * @param string $stylesheet_or_template The stylesheet or template name of the theme
682  * @return string Themes URI.
683  */
684 function get_theme_root_uri( $stylesheet_or_template = false ) {
685         if ( $stylesheet_or_template ) {
686                 if ( $theme_root = get_raw_theme_root($stylesheet_or_template) )
687                         $theme_root_uri = content_url( $theme_root );
688                 else
689                         $theme_root_uri = content_url( 'themes' );
690         } else {
691                 $theme_root_uri = content_url( 'themes' );
692         }
693
694         return apply_filters( 'theme_root_uri', $theme_root_uri, get_option('siteurl'), $stylesheet_or_template );
695 }
696
697 /**
698  * Get the raw theme root relative to the content directory with no filters applied.
699  *
700  * @since 3.1.0
701  *
702  * @param string $stylesheet_or_template The stylesheet or template name of the theme
703  * @return string Theme root
704  */
705 function get_raw_theme_root( $stylesheet_or_template, $no_cache = false ) {
706         global $wp_theme_directories;
707
708         if ( count($wp_theme_directories) <= 1 )
709                 return '/themes';
710
711         $theme_root = false;
712
713         // If requesting the root for the current theme, consult options to avoid calling get_theme_roots()
714         if ( !$no_cache ) {
715                 if ( get_option('stylesheet') == $stylesheet_or_template )
716                         $theme_root = get_option('stylesheet_root');
717                 elseif ( get_option('template') == $stylesheet_or_template )
718                         $theme_root = get_option('template_root');
719         }
720
721         if ( empty($theme_root) ) {
722                 $theme_roots = get_theme_roots();
723                 if ( !empty($theme_roots[$stylesheet_or_template]) )
724                         $theme_root = $theme_roots[$stylesheet_or_template];
725         }
726
727         return $theme_root;
728 }
729
730 /**
731  * Retrieve path to a template
732  *
733  * Used to quickly retrieve the path of a template without including the file
734  * extension. It will also check the parent theme, if the file exists, with
735  * the use of {@link locate_template()}. Allows for more generic template location
736  * without the use of the other get_*_template() functions.
737  *
738  * @since 1.5.0
739  *
740  * @param string $type Filename without extension.
741  * @param array $templates An optional list of template candidates
742  * @return string Full path to file.
743  */
744 function get_query_template( $type, $templates = array() ) {
745         $type = preg_replace( '|[^a-z0-9-]+|', '', $type );
746
747         if ( empty( $templates ) )
748                 $templates = array("{$type}.php");
749
750         return apply_filters( "{$type}_template", locate_template( $templates ) );
751 }
752
753 /**
754  * Retrieve path of index template in current or parent template.
755  *
756  * @since 3.0.0
757  *
758  * @return string
759  */
760 function get_index_template() {
761         return get_query_template('index');
762 }
763
764 /**
765  * Retrieve path of 404 template in current or parent template.
766  *
767  * @since 1.5.0
768  *
769  * @return string
770  */
771 function get_404_template() {
772         return get_query_template('404');
773 }
774
775 /**
776  * Retrieve path of archive template in current or parent template.
777  *
778  * @since 1.5.0
779  *
780  * @return string
781  */
782 function get_archive_template() {
783         $post_type = get_query_var( 'post_type' );
784
785         $templates = array();
786
787         if ( $post_type )
788                 $templates[] = "archive-{$post_type}.php";
789         $templates[] = 'archive.php';
790
791         return get_query_template( 'archive', $templates );
792 }
793
794 /**
795  * Retrieve path of author template in current or parent template.
796  *
797  * @since 1.5.0
798  *
799  * @return string
800  */
801 function get_author_template() {
802         $author = get_queried_object();
803
804         $templates = array();
805
806         $templates[] = "author-{$author->user_nicename}.php";
807         $templates[] = "author-{$author->ID}.php";
808         $templates[] = 'author.php';
809
810         return get_query_template( 'author', $templates );
811 }
812
813 /**
814  * Retrieve path of category template in current or parent template.
815  *
816  * Works by first retrieving the current slug for example 'category-default.php' and then
817  * trying category ID, for example 'category-1.php' and will finally fallback to category.php
818  * template, if those files don't exist.
819  *
820  * @since 1.5.0
821  * @uses apply_filters() Calls 'category_template' on file path of category template.
822  *
823  * @return string
824  */
825 function get_category_template() {
826         $category = get_queried_object();
827
828         $templates = array();
829
830         $templates[] = "category-{$category->slug}.php";
831         $templates[] = "category-{$category->term_id}.php";
832         $templates[] = 'category.php';
833
834         return get_query_template( 'category', $templates );
835 }
836
837 /**
838  * Retrieve path of tag template in current or parent template.
839  *
840  * Works by first retrieving the current tag name, for example 'tag-wordpress.php' and then
841  * trying tag ID, for example 'tag-1.php' and will finally fallback to tag.php
842  * template, if those files don't exist.
843  *
844  * @since 2.3.0
845  * @uses apply_filters() Calls 'tag_template' on file path of tag template.
846  *
847  * @return string
848  */
849 function get_tag_template() {
850         $tag = get_queried_object();
851
852         $templates = array();
853
854         $templates[] = "tag-{$tag->slug}.php";
855         $templates[] = "tag-{$tag->term_id}.php";
856         $templates[] = 'tag.php';
857
858         return get_query_template( 'tag', $templates );
859 }
860
861 /**
862  * Retrieve path of taxonomy template in current or parent template.
863  *
864  * Retrieves the taxonomy and term, if term is available. The template is
865  * prepended with 'taxonomy-' and followed by both the taxonomy string and
866  * the taxonomy string followed by a dash and then followed by the term.
867  *
868  * The taxonomy and term template is checked and used first, if it exists.
869  * Second, just the taxonomy template is checked, and then finally, taxonomy.php
870  * template is used. If none of the files exist, then it will fall back on to
871  * index.php.
872  *
873  * @since 2.5.0
874  * @uses apply_filters() Calls 'taxonomy_template' filter on found path.
875  *
876  * @return string
877  */
878 function get_taxonomy_template() {
879         $term = get_queried_object();
880         $taxonomy = $term->taxonomy;
881
882         $templates = array();
883
884         $templates[] = "taxonomy-$taxonomy-{$term->slug}.php";
885         $templates[] = "taxonomy-$taxonomy.php";
886         $templates[] = 'taxonomy.php';
887
888         return get_query_template( 'taxonomy', $templates );
889 }
890
891 /**
892  * Retrieve path of date template in current or parent template.
893  *
894  * @since 1.5.0
895  *
896  * @return string
897  */
898 function get_date_template() {
899         return get_query_template('date');
900 }
901
902 /**
903  * Retrieve path of home template in current or parent template.
904  *
905  * This is the template used for the page containing the blog posts
906  *
907  * Attempts to locate 'home.php' first before falling back to 'index.php'.
908  *
909  * @since 1.5.0
910  * @uses apply_filters() Calls 'home_template' on file path of home template.
911  *
912  * @return string
913  */
914 function get_home_template() {
915         $templates = array( 'home.php', 'index.php' );
916
917         return get_query_template( 'home', $templates );
918 }
919
920 /**
921  * Retrieve path of front-page template in current or parent template.
922  *
923  * Looks for 'front-page.php'.
924  *
925  * @since 3.0.0
926  * @uses apply_filters() Calls 'front_page_template' on file path of template.
927  *
928  * @return string
929  */
930 function get_front_page_template() {
931         $templates = array('front-page.php');
932
933         return get_query_template( 'front_page', $templates );
934 }
935
936 /**
937  * Retrieve path of page template in current or parent template.
938  *
939  * Will first look for the specifically assigned page template
940  * The will search for 'page-{slug}.php' followed by 'page-id.php'
941  * and finally 'page.php'
942  *
943  * @since 1.5.0
944  *
945  * @return string
946  */
947 function get_page_template() {
948         $id = get_queried_object_id();
949         $template = get_post_meta($id, '_wp_page_template', true);
950         $pagename = get_query_var('pagename');
951
952         if ( !$pagename && $id > 0 ) {
953                 // If a static page is set as the front page, $pagename will not be set. Retrieve it from the queried object
954                 $post = get_queried_object();
955                 $pagename = $post->post_name;
956         }
957
958         if ( 'default' == $template )
959                 $template = '';
960
961         $templates = array();
962         if ( !empty($template) && !validate_file($template) )
963                 $templates[] = $template;
964         if ( $pagename )
965                 $templates[] = "page-$pagename.php";
966         if ( $id )
967                 $templates[] = "page-$id.php";
968         $templates[] = 'page.php';
969
970         return get_query_template( 'page', $templates );
971 }
972
973 /**
974  * Retrieve path of paged template in current or parent template.
975  *
976  * @since 1.5.0
977  *
978  * @return string
979  */
980 function get_paged_template() {
981         return get_query_template('paged');
982 }
983
984 /**
985  * Retrieve path of search template in current or parent template.
986  *
987  * @since 1.5.0
988  *
989  * @return string
990  */
991 function get_search_template() {
992         return get_query_template('search');
993 }
994
995 /**
996  * Retrieve path of single template in current or parent template.
997  *
998  * @since 1.5.0
999  *
1000  * @return string
1001  */
1002 function get_single_template() {
1003         $object = get_queried_object();
1004
1005         $templates = array();
1006
1007         $templates[] = "single-{$object->post_type}.php";
1008         $templates[] = "single.php";
1009
1010         return get_query_template( 'single', $templates );
1011 }
1012
1013 /**
1014  * Retrieve path of attachment template in current or parent template.
1015  *
1016  * The attachment path first checks if the first part of the mime type exists.
1017  * The second check is for the second part of the mime type. The last check is
1018  * for both types separated by an underscore. If neither are found then the file
1019  * 'attachment.php' is checked and returned.
1020  *
1021  * Some examples for the 'text/plain' mime type are 'text.php', 'plain.php', and
1022  * finally 'text_plain.php'.
1023  *
1024  * @since 2.0.0
1025  *
1026  * @return string
1027  */
1028 function get_attachment_template() {
1029         global $posts;
1030         $type = explode('/', $posts[0]->post_mime_type);
1031         if ( $template = get_query_template($type[0]) )
1032                 return $template;
1033         elseif ( $template = get_query_template($type[1]) )
1034                 return $template;
1035         elseif ( $template = get_query_template("$type[0]_$type[1]") )
1036                 return $template;
1037         else
1038                 return get_query_template('attachment');
1039 }
1040
1041 /**
1042  * Retrieve path of comment popup template in current or parent template.
1043  *
1044  * Checks for comment popup template in current template, if it exists or in the
1045  * parent template.
1046  *
1047  * @since 1.5.0
1048  * @uses apply_filters() Calls 'comments_popup_template' filter on path.
1049  *
1050  * @return string
1051  */
1052 function get_comments_popup_template() {
1053         $template = get_query_template( 'comments_popup', array( 'comments-popup.php' ) );
1054
1055         // Backward compat code will be removed in a future release
1056         if ('' == $template)
1057                 $template = ABSPATH . WPINC . '/theme-compat/comments-popup.php';
1058
1059         return $template;
1060 }
1061
1062 /**
1063  * Retrieve the name of the highest priority template file that exists.
1064  *
1065  * Searches in the STYLESHEETPATH before TEMPLATEPATH so that themes which
1066  * inherit from a parent theme can just overload one file.
1067  *
1068  * @since 2.7.0
1069  *
1070  * @param string|array $template_names Template file(s) to search for, in order.
1071  * @param bool $load If true the template file will be loaded if it is found.
1072  * @param bool $require_once Whether to require_once or require. Default true. Has no effect if $load is false.
1073  * @return string The template filename if one is located.
1074  */
1075 function locate_template($template_names, $load = false, $require_once = true ) {
1076         $located = '';
1077         foreach ( (array) $template_names as $template_name ) {
1078                 if ( !$template_name )
1079                         continue;
1080                 if ( file_exists(STYLESHEETPATH . '/' . $template_name)) {
1081                         $located = STYLESHEETPATH . '/' . $template_name;
1082                         break;
1083                 } else if ( file_exists(TEMPLATEPATH . '/' . $template_name) ) {
1084                         $located = TEMPLATEPATH . '/' . $template_name;
1085                         break;
1086                 }
1087         }
1088
1089         if ( $load && '' != $located )
1090                 load_template( $located, $require_once );
1091
1092         return $located;
1093 }
1094
1095 /**
1096  * Require the template file with WordPress environment.
1097  *
1098  * The globals are set up for the template file to ensure that the WordPress
1099  * environment is available from within the function. The query variables are
1100  * also available.
1101  *
1102  * @since 1.5.0
1103  *
1104  * @param string $_template_file Path to template file.
1105  * @param bool $require_once Whether to require_once or require. Default true.
1106  */
1107 function load_template( $_template_file, $require_once = true ) {
1108         global $posts, $post, $wp_did_header, $wp_did_template_redirect, $wp_query, $wp_rewrite, $wpdb, $wp_version, $wp, $id, $comment, $user_ID;
1109
1110         if ( is_array( $wp_query->query_vars ) )
1111                 extract( $wp_query->query_vars, EXTR_SKIP );
1112
1113         if ( $require_once )
1114                 require_once( $_template_file );
1115         else
1116                 require( $_template_file );
1117 }
1118
1119 /**
1120  * Display localized stylesheet link element.
1121  *
1122  * @since 2.1.0
1123  */
1124 function locale_stylesheet() {
1125         $stylesheet = get_locale_stylesheet_uri();
1126         if ( empty($stylesheet) )
1127                 return;
1128         echo '<link rel="stylesheet" href="' . $stylesheet . '" type="text/css" media="screen" />';
1129 }
1130
1131 /**
1132  * Start preview theme output buffer.
1133  *
1134  * Will only preform task if the user has permissions and template and preview
1135  * query variables exist.
1136  *
1137  * @since 2.6.0
1138  */
1139 function preview_theme() {
1140         if ( ! (isset($_GET['template']) && isset($_GET['preview'])) )
1141                 return;
1142
1143         if ( !current_user_can( 'switch_themes' ) )
1144                 return;
1145
1146         // Admin Thickbox requests
1147         if ( isset( $_GET['preview_iframe'] ) )
1148                 show_admin_bar( false );
1149
1150         $_GET['template'] = preg_replace('|[^a-z0-9_./-]|i', '', $_GET['template']);
1151
1152         if ( validate_file($_GET['template']) )
1153                 return;
1154
1155         add_filter( 'template', '_preview_theme_template_filter' );
1156
1157         if ( isset($_GET['stylesheet']) ) {
1158                 $_GET['stylesheet'] = preg_replace('|[^a-z0-9_./-]|i', '', $_GET['stylesheet']);
1159                 if ( validate_file($_GET['stylesheet']) )
1160                         return;
1161                 add_filter( 'stylesheet', '_preview_theme_stylesheet_filter' );
1162         }
1163
1164         // Prevent theme mods to current theme being used on theme being previewed
1165         add_filter( 'pre_option_mods_' . get_current_theme(), '__return_empty_array' );
1166
1167         ob_start( 'preview_theme_ob_filter' );
1168 }
1169 add_action('setup_theme', 'preview_theme');
1170
1171 /**
1172  * Private function to modify the current template when previewing a theme
1173  *
1174  * @since 2.9.0
1175  * @access private
1176  *
1177  * @return string
1178  */
1179 function _preview_theme_template_filter() {
1180         return isset($_GET['template']) ? $_GET['template'] : '';
1181 }
1182
1183 /**
1184  * Private function to modify the current stylesheet when previewing a theme
1185  *
1186  * @since 2.9.0
1187  * @access private
1188  *
1189  * @return string
1190  */
1191 function _preview_theme_stylesheet_filter() {
1192         return isset($_GET['stylesheet']) ? $_GET['stylesheet'] : '';
1193 }
1194
1195 /**
1196  * Callback function for ob_start() to capture all links in the theme.
1197  *
1198  * @since 2.6.0
1199  * @access private
1200  *
1201  * @param string $content
1202  * @return string
1203  */
1204 function preview_theme_ob_filter( $content ) {
1205         return preg_replace_callback( "|(<a.*?href=([\"']))(.*?)([\"'].*?>)|", 'preview_theme_ob_filter_callback', $content );
1206 }
1207
1208 /**
1209  * Manipulates preview theme links in order to control and maintain location.
1210  *
1211  * Callback function for preg_replace_callback() to accept and filter matches.
1212  *
1213  * @since 2.6.0
1214  * @access private
1215  *
1216  * @param array $matches
1217  * @return string
1218  */
1219 function preview_theme_ob_filter_callback( $matches ) {
1220         if ( strpos($matches[4], 'onclick') !== false )
1221                 $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.
1222         if (
1223                 ( false !== strpos($matches[3], '/wp-admin/') )
1224         ||
1225                 ( false !== strpos( $matches[3], '://' ) && 0 !== strpos( $matches[3], home_url() ) )
1226         ||
1227                 ( false !== strpos($matches[3], '/feed/') )
1228         ||
1229                 ( false !== strpos($matches[3], '/trackback/') )
1230         )
1231                 return $matches[1] . "#$matches[2] onclick=$matches[2]return false;" . $matches[4];
1232
1233         $link = add_query_arg( array('preview' => 1, 'template' => $_GET['template'], 'stylesheet' => @$_GET['stylesheet'] ), $matches[3] );
1234         if ( 0 === strpos($link, 'preview=1') )
1235                 $link = "?$link";
1236         return $matches[1] . esc_attr( $link ) . $matches[4];
1237 }
1238
1239 /**
1240  * Switches current theme to new template and stylesheet names.
1241  *
1242  * @since 2.5.0
1243  * @uses do_action() Calls 'switch_theme' action on updated theme display name.
1244  *
1245  * @param string $template Template name
1246  * @param string $stylesheet Stylesheet name.
1247  */
1248 function switch_theme($template, $stylesheet) {
1249         global $wp_theme_directories;
1250
1251         update_option('template', $template);
1252         update_option('stylesheet', $stylesheet);
1253         if ( count($wp_theme_directories) > 1 ) {
1254                 update_option('template_root', get_raw_theme_root($template, true));
1255                 update_option('stylesheet_root', get_raw_theme_root($stylesheet, true));
1256         }
1257         delete_option('current_theme');
1258         $theme = get_current_theme();
1259         if ( is_admin() && false === get_option( "theme_mods_$stylesheet" ) ) {
1260                 $default_theme_mods = (array) get_option( "mods_$theme" );
1261                 add_option( "theme_mods_$stylesheet", $default_theme_mods );
1262         }
1263         do_action('switch_theme', $theme);
1264 }
1265
1266 /**
1267  * Checks that current theme files 'index.php' and 'style.css' exists.
1268  *
1269  * Does not check the default theme, which is the fallback and should always exist.
1270  * Will switch theme to the fallback theme if current theme does not validate.
1271  * You can use the 'validate_current_theme' filter to return FALSE to
1272  * disable this functionality.
1273  *
1274  * @since 1.5.0
1275  * @see WP_DEFAULT_THEME
1276  *
1277  * @return bool
1278  */
1279 function validate_current_theme() {
1280         // Don't validate during an install/upgrade.
1281         if ( defined('WP_INSTALLING') || !apply_filters( 'validate_current_theme', true ) )
1282                 return true;
1283
1284         if ( get_template() != WP_DEFAULT_THEME && !file_exists(get_template_directory() . '/index.php') ) {
1285                 switch_theme( WP_DEFAULT_THEME, WP_DEFAULT_THEME );
1286                 return false;
1287         }
1288
1289         if ( get_stylesheet() != WP_DEFAULT_THEME && !file_exists(get_template_directory() . '/style.css') ) {
1290                 switch_theme( WP_DEFAULT_THEME, WP_DEFAULT_THEME );
1291                 return false;
1292         }
1293
1294         if ( is_child_theme() && ! file_exists( get_stylesheet_directory() . '/style.css' ) ) {
1295                 switch_theme( WP_DEFAULT_THEME, WP_DEFAULT_THEME );
1296                 return false;
1297         }
1298
1299         return true;
1300 }
1301
1302 /**
1303  * Retrieve all theme modifications.
1304  *
1305  * @since 3.1.0
1306  *
1307  * @return array Theme modifications.
1308  */
1309 function get_theme_mods() {
1310         $theme_slug = get_option( 'stylesheet' );
1311         if ( false === ( $mods = get_option( "theme_mods_$theme_slug" ) ) ) {
1312                 $theme_name = get_current_theme();
1313                 $mods = get_option( "mods_$theme_name" ); // Deprecated location.
1314                 if ( is_admin() && false !== $mods ) {
1315                         update_option( "theme_mods_$theme_slug", $mods );
1316                         delete_option( "mods_$theme_name" );
1317                 }
1318         }
1319         return $mods;
1320 }
1321
1322 /**
1323  * Retrieve theme modification value for the current theme.
1324  *
1325  * If the modification name does not exist, then the $default will be passed
1326  * through {@link http://php.net/sprintf sprintf()} PHP function with the first
1327  * string the template directory URI and the second string the stylesheet
1328  * directory URI.
1329  *
1330  * @since 2.1.0
1331  * @uses apply_filters() Calls 'theme_mod_$name' filter on the value.
1332  *
1333  * @param string $name Theme modification name.
1334  * @param bool|string $default
1335  * @return string
1336  */
1337 function get_theme_mod( $name, $default = false ) {
1338         $mods = get_theme_mods();
1339
1340         if ( isset( $mods[ $name ] ) )
1341                 return apply_filters( "theme_mod_$name", $mods[ $name ] );
1342
1343         return apply_filters( "theme_mod_$name", sprintf( $default, get_template_directory_uri(), get_stylesheet_directory_uri() ) );
1344 }
1345
1346 /**
1347  * Update theme modification value for the current theme.
1348  *
1349  * @since 2.1.0
1350  *
1351  * @param string $name Theme modification name.
1352  * @param string $value theme modification value.
1353  */
1354 function set_theme_mod( $name, $value ) {
1355         $mods = get_theme_mods();
1356
1357         $mods[ $name ] = $value;
1358
1359         $theme = get_option( 'stylesheet' );
1360         update_option( "theme_mods_$theme", $mods );
1361 }
1362
1363 /**
1364  * Remove theme modification name from current theme list.
1365  *
1366  * If removing the name also removes all elements, then the entire option will
1367  * be removed.
1368  *
1369  * @since 2.1.0
1370  *
1371  * @param string $name Theme modification name.
1372  * @return null
1373  */
1374 function remove_theme_mod( $name ) {
1375         $mods = get_theme_mods();
1376
1377         if ( ! isset( $mods[ $name ] ) )
1378                 return;
1379
1380         unset( $mods[ $name ] );
1381
1382         if ( empty( $mods ) )
1383                 return remove_theme_mods();
1384
1385         $theme = get_option( 'stylesheet' );
1386         update_option( "theme_mods_$theme", $mods );
1387 }
1388
1389 /**
1390  * Remove theme modifications option for current theme.
1391  *
1392  * @since 2.1.0
1393  */
1394 function remove_theme_mods() {
1395         delete_option( 'theme_mods_' . get_option( 'stylesheet' ) );
1396         delete_option( 'mods_' . get_current_theme() );
1397 }
1398
1399 /**
1400  * Retrieve text color for custom header.
1401  *
1402  * @since 2.1.0
1403  * @uses HEADER_TEXTCOLOR
1404  *
1405  * @return string
1406  */
1407 function get_header_textcolor() {
1408         $default = defined('HEADER_TEXTCOLOR') ? HEADER_TEXTCOLOR : '';
1409
1410         return get_theme_mod('header_textcolor', $default);
1411 }
1412
1413 /**
1414  * Display text color for custom header.
1415  *
1416  * @since 2.1.0
1417  */
1418 function header_textcolor() {
1419         echo get_header_textcolor();
1420 }
1421
1422 /**
1423  * Retrieve header image for custom header.
1424  *
1425  * @since 2.1.0
1426  * @uses HEADER_IMAGE
1427  *
1428  * @return string
1429  */
1430 function get_header_image() {
1431         $default = defined( 'HEADER_IMAGE' ) ? HEADER_IMAGE : '';
1432         $url = get_theme_mod( 'header_image', $default );
1433
1434         if ( 'remove-header' == $url )
1435                 return false;
1436
1437         if ( is_random_header_image() )
1438                 $url = get_random_header_image();
1439
1440         if ( is_ssl() )
1441                 $url = str_replace( 'http://', 'https://', $url );
1442         else
1443                 $url = str_replace( 'https://', 'http://', $url );
1444
1445         return esc_url_raw( $url );
1446 }
1447
1448 /**
1449  * Get random header image from registered images in theme.
1450  *
1451  * @since 3.2.0
1452  *
1453  * @return string Path to header image
1454  */
1455 function get_random_header_image() {
1456         global $_wp_default_headers;
1457
1458         $header_image_mod = get_theme_mod( 'header_image', '' );
1459         $headers = array();
1460
1461         if ( 'random-uploaded-image' == $header_image_mod )
1462                 $headers = get_uploaded_header_images();
1463         elseif ( ! empty( $_wp_default_headers ) ) {
1464                 if ( 'random-default-image' == $header_image_mod ) {
1465                         $headers = $_wp_default_headers;
1466                 } else {
1467                         $is_random = get_theme_support( 'custom-header' );
1468                         if ( isset( $is_random[ 0 ] ) && !empty( $is_random[ 0 ][ 'random-default' ] ) )
1469                                 $headers = $_wp_default_headers;
1470                 }
1471         }
1472
1473         if ( empty( $headers ) )
1474                 return '';
1475
1476         $random_image = array_rand( $headers );
1477         $header_url = sprintf( $headers[$random_image]['url'], get_template_directory_uri(), get_stylesheet_directory_uri() );
1478
1479         return $header_url;
1480 }
1481
1482 /**
1483  * Check if random header image is in use.
1484  *
1485  * Always true if user expressly chooses the option in Appearance > Header.
1486  * Also true if theme has multiple header images registered, no specific header image
1487  * is chosen, and theme turns on random headers with add_theme_support().
1488  *
1489  * @since 3.2.0
1490  * @uses HEADER_IMAGE
1491  *
1492  * @param string $type The random pool to use. any|default|uploaded
1493  * @return boolean
1494  */
1495 function is_random_header_image( $type = 'any' ) {
1496         $default = defined( 'HEADER_IMAGE' ) ? HEADER_IMAGE : '';
1497         $header_image_mod = get_theme_mod( 'header_image', $default );
1498
1499         if ( 'any' == $type ) {
1500                 if ( 'random-default-image' == $header_image_mod || 'random-uploaded-image' == $header_image_mod || ( '' != get_random_header_image() && empty( $header_image_mod ) ) )
1501                         return true;
1502         } else {
1503                 if ( "random-$type-image" == $header_image_mod )
1504                         return true;
1505                 elseif ( 'default' == $type && empty( $header_image_mod ) && '' != get_random_header_image() )
1506                         return true;
1507         }
1508
1509         return false;
1510 }
1511
1512 /**
1513  * Display header image path.
1514  *
1515  * @since 2.1.0
1516  */
1517 function header_image() {
1518         echo get_header_image();
1519 }
1520
1521 /**
1522  * Get the header images uploaded for the current theme.
1523  *
1524  * @since 3.2.0
1525  *
1526  * @return array
1527  */
1528 function get_uploaded_header_images() {
1529         $header_images = array();
1530
1531         // @todo caching
1532         $headers = get_posts( array( 'post_type' => 'attachment', 'meta_key' => '_wp_attachment_is_custom_header', 'meta_value' => get_option('stylesheet'), 'orderby' => 'none', 'nopaging' => true ) );
1533
1534         if ( empty( $headers ) )
1535                 return array();
1536
1537         foreach ( (array) $headers as $header ) {
1538                 $url = esc_url_raw( $header->guid );
1539                 $header = basename($url);
1540                 $header_images[$header] = array();
1541                 $header_images[$header]['url'] =  $url;
1542                 $header_images[$header]['thumbnail_url'] =  $url;
1543                 $header_images[$header]['uploaded'] = true;
1544         }
1545
1546         return $header_images;
1547 }
1548
1549 /**
1550  * Add callbacks for image header display.
1551  *
1552  * The parameter $header_callback callback will be required to display the
1553  * content for the 'wp_head' action. The parameter $admin_header_callback
1554  * callback will be added to Custom_Image_Header class and that will be added
1555  * to the 'admin_menu' action.
1556  *
1557  * @since 2.1.0
1558  * @uses Custom_Image_Header Sets up for $admin_header_callback for administration panel display.
1559  *
1560  * @param callback $header_callback Call on 'wp_head' action.
1561  * @param callback $admin_header_callback Call on custom header administration screen.
1562  * @param callback $admin_image_div_callback Output a custom header image div on the custom header administration screen. Optional.
1563  */
1564 function add_custom_image_header( $header_callback, $admin_header_callback, $admin_image_div_callback = '' ) {
1565         if ( ! empty( $header_callback ) )
1566                 add_action('wp_head', $header_callback);
1567
1568         $support = array( 'callback' => $header_callback );
1569         $theme_support = get_theme_support( 'custom-header' );
1570         if ( ! empty( $theme_support ) && is_array( $theme_support[ 0 ] ) )
1571                 $support = array_merge( $theme_support[ 0 ], $support );
1572         add_theme_support( 'custom-header',  $support );
1573         add_theme_support( 'custom-header-uploads' );
1574
1575         if ( ! is_admin() )
1576                 return;
1577
1578         global $custom_image_header;
1579
1580         require_once( ABSPATH . 'wp-admin/custom-header.php' );
1581         $custom_image_header = new Custom_Image_Header( $admin_header_callback, $admin_image_div_callback );
1582         add_action( 'admin_menu', array( &$custom_image_header, 'init' ) );
1583 }
1584
1585 /**
1586  * Remove image header support.
1587  *
1588  * @since 3.1.0
1589  * @see add_custom_image_header()
1590  *
1591  * @return bool Whether support was removed.
1592  */
1593 function remove_custom_image_header() {
1594         if ( ! current_theme_supports( 'custom-header' ) )
1595                 return false;
1596
1597         $callback = get_theme_support( 'custom-header' );
1598         remove_action( 'wp_head', $callback[0]['callback'] );
1599         _remove_theme_support( 'custom-header' );
1600         remove_theme_support( 'custom-header-uploads' );
1601
1602         if ( is_admin() ) {
1603                 remove_action( 'admin_menu', array( &$GLOBALS['custom_image_header'], 'init' ) );
1604                 unset( $GLOBALS['custom_image_header'] );
1605         }
1606
1607         return true;
1608 }
1609
1610 /**
1611  * Register a selection of default headers to be displayed by the custom header admin UI.
1612  *
1613  * @since 3.0.0
1614  *
1615  * @param array $headers Array of headers keyed by a string id. The ids point to arrays containing 'url', 'thumbnail_url', and 'description' keys.
1616  */
1617 function register_default_headers( $headers ) {
1618         global $_wp_default_headers;
1619
1620         $_wp_default_headers = array_merge( (array) $_wp_default_headers, (array) $headers );
1621 }
1622
1623 /**
1624  * Unregister default headers.
1625  *
1626  * This function must be called after register_default_headers() has already added the
1627  * header you want to remove.
1628  *
1629  * @see register_default_headers()
1630  * @since 3.0.0
1631  *
1632  * @param string|array $header The header string id (key of array) to remove, or an array thereof.
1633  * @return True on success, false on failure.
1634  */
1635 function unregister_default_headers( $header ) {
1636         global $_wp_default_headers;
1637         if ( is_array( $header ) ) {
1638                 array_map( 'unregister_default_headers', $header );
1639         } elseif ( isset( $_wp_default_headers[ $header ] ) ) {
1640                 unset( $_wp_default_headers[ $header ] );
1641                 return true;
1642         } else {
1643                 return false;
1644         }
1645 }
1646
1647 /**
1648  * Retrieve background image for custom background.
1649  *
1650  * @since 3.0.0
1651  *
1652  * @return string
1653  */
1654 function get_background_image() {
1655         $default = defined('BACKGROUND_IMAGE') ? BACKGROUND_IMAGE : '';
1656
1657         return get_theme_mod('background_image', $default);
1658 }
1659
1660 /**
1661  * Display background image path.
1662  *
1663  * @since 3.0.0
1664  */
1665 function background_image() {
1666         echo get_background_image();
1667 }
1668
1669 /**
1670  * Retrieve value for custom background color.
1671  *
1672  * @since 3.0.0
1673  * @uses BACKGROUND_COLOR
1674  *
1675  * @return string
1676  */
1677 function get_background_color() {
1678         $default = defined('BACKGROUND_COLOR') ? BACKGROUND_COLOR : '';
1679
1680         return get_theme_mod('background_color', $default);
1681 }
1682
1683 /**
1684  * Display background color value.
1685  *
1686  * @since 3.0.0
1687  */
1688 function background_color() {
1689         echo get_background_color();
1690 }
1691
1692 /**
1693  * Add callbacks for background image display.
1694  *
1695  * The parameter $header_callback callback will be required to display the
1696  * content for the 'wp_head' action. The parameter $admin_header_callback
1697  * callback will be added to Custom_Background class and that will be added
1698  * to the 'admin_menu' action.
1699  *
1700  * @since 3.0.0
1701  * @uses Custom_Background Sets up for $admin_header_callback for administration panel display.
1702  *
1703  * @param callback $header_callback Call on 'wp_head' action.
1704  * @param callback $admin_header_callback Call on custom background administration screen.
1705  * @param callback $admin_image_div_callback Output a custom background image div on the custom background administration screen. Optional.
1706  */
1707 function add_custom_background( $header_callback = '', $admin_header_callback = '', $admin_image_div_callback = '' ) {
1708         if ( isset( $GLOBALS['custom_background'] ) )
1709                 return;
1710
1711         if ( empty( $header_callback ) )
1712                 $header_callback = '_custom_background_cb';
1713
1714         add_action( 'wp_head', $header_callback );
1715
1716         add_theme_support( 'custom-background', array( 'callback' => $header_callback ) );
1717
1718         if ( ! is_admin() )
1719                 return;
1720         require_once( ABSPATH . 'wp-admin/custom-background.php' );
1721         $GLOBALS['custom_background'] =& new Custom_Background( $admin_header_callback, $admin_image_div_callback );
1722         add_action( 'admin_menu', array( &$GLOBALS['custom_background'], 'init' ) );
1723 }
1724
1725 /**
1726  * Remove custom background support.
1727  *
1728  * @since 3.1.0
1729  * @see add_custom_background()
1730  *
1731  * @return bool Whether support was removed.
1732  */
1733 function remove_custom_background() {
1734         if ( ! current_theme_supports( 'custom-background' ) )
1735                 return false;
1736
1737         $callback = get_theme_support( 'custom-background' );
1738         remove_action( 'wp_head', $callback[0]['callback'] );
1739         _remove_theme_support( 'custom-background' );
1740
1741         if ( is_admin() ) {
1742                 remove_action( 'admin_menu', array( &$GLOBALS['custom_background'], 'init' ) );
1743                 unset( $GLOBALS['custom_background'] );
1744         }
1745
1746         return true;
1747 }
1748
1749 /**
1750  * Default custom background callback.
1751  *
1752  * @since 3.0.0
1753  * @see add_custom_background()
1754  * @access protected
1755  */
1756 function _custom_background_cb() {
1757         $background = get_background_image();
1758         $color = get_background_color();
1759         if ( ! $background && ! $color )
1760                 return;
1761
1762         $style = $color ? "background-color: #$color;" : '';
1763
1764         if ( $background ) {
1765                 $image = " background-image: url('$background');";
1766
1767                 $repeat = get_theme_mod( 'background_repeat', 'repeat' );
1768                 if ( ! in_array( $repeat, array( 'no-repeat', 'repeat-x', 'repeat-y', 'repeat' ) ) )
1769                         $repeat = 'repeat';
1770                 $repeat = " background-repeat: $repeat;";
1771
1772                 $position = get_theme_mod( 'background_position_x', 'left' );
1773                 if ( ! in_array( $position, array( 'center', 'right', 'left' ) ) )
1774                         $position = 'left';
1775                 $position = " background-position: top $position;";
1776
1777                 $attachment = get_theme_mod( 'background_attachment', 'scroll' );
1778                 if ( ! in_array( $attachment, array( 'fixed', 'scroll' ) ) )
1779                         $attachment = 'scroll';
1780                 $attachment = " background-attachment: $attachment;";
1781
1782                 $style .= $image . $repeat . $position . $attachment;
1783         }
1784 ?>
1785 <style type="text/css">
1786 body { <?php echo trim( $style ); ?> }
1787 </style>
1788 <?php
1789 }
1790
1791 /**
1792  * Add callback for custom TinyMCE editor stylesheets.
1793  *
1794  * The parameter $stylesheet is the name of the stylesheet, relative to
1795  * the theme root. It also accepts an array of stylesheets.
1796  * It is optional and defaults to 'editor-style.css'.
1797  *
1798  * Supports RTL stylesheets automatically by searching for the -rtl prefix, e.g.
1799  * editor-style-rtl.css. If an array of stylesheets is passed to add_editor_style(),
1800  * RTL is only added for the first stylesheet.
1801  *
1802  * @since 3.0.0
1803  *
1804  * @param mixed $stylesheet Optional. Stylesheet name or array thereof, relative to theme root.
1805  *      Defaults to 'editor-style.css'
1806  */
1807 function add_editor_style( $stylesheet = 'editor-style.css' ) {
1808
1809         add_theme_support( 'editor-style' );
1810
1811         if ( ! is_admin() )
1812                 return;
1813
1814         global $editor_styles;
1815         $editor_styles = (array) $editor_styles;
1816         $stylesheet    = (array) $stylesheet;
1817         if ( is_rtl() ) {
1818                 $rtl_stylesheet = str_replace('.css', '-rtl.css', $stylesheet[0]);
1819                 $stylesheet[] = $rtl_stylesheet;
1820         }
1821
1822         $editor_styles = array_merge( $editor_styles, $stylesheet );
1823 }
1824
1825 /**
1826  * Removes all visual editor stylesheets.
1827  *
1828  * @since 3.1.0
1829  *
1830  * @return bool True on success, false if there were no stylesheets to remove.
1831  */
1832 function remove_editor_styles() {
1833         if ( ! current_theme_supports( 'editor-style' ) )
1834                 return false;
1835         _remove_theme_support( 'editor-style' );
1836         if ( is_admin() )
1837                 $GLOBALS['editor_styles'] = array();
1838         return true;
1839 }
1840
1841 /**
1842  * Allows a theme to register its support of a certain feature
1843  *
1844  * Must be called in the theme's functions.php file to work.
1845  * If attached to a hook, it must be after_setup_theme.
1846  * The init hook may be too late for some features.
1847  *
1848  * @since 2.9.0
1849  * @param string $feature the feature being added
1850  */
1851 function add_theme_support( $feature ) {
1852         global $_wp_theme_features;
1853
1854         if ( func_num_args() == 1 )
1855                 $_wp_theme_features[$feature] = true;
1856         else
1857                 $_wp_theme_features[$feature] = array_slice( func_get_args(), 1 );
1858
1859         if ( $feature == 'post-formats' && is_array( $_wp_theme_features[$feature][0] ) )
1860                 $_wp_theme_features[$feature][0] = array_intersect( $_wp_theme_features[$feature][0], array_keys( get_post_format_slugs() ) );
1861 }
1862
1863 /**
1864  * Gets the theme support arguments passed when registering that support
1865  *
1866  * @since 3.1
1867  * @param string $feature the feature to check
1868  * @return array The array of extra arguments
1869  */
1870 function get_theme_support( $feature ) {
1871         global $_wp_theme_features;
1872         if ( !isset( $_wp_theme_features[$feature] ) )
1873                 return false;
1874         else
1875                 return $_wp_theme_features[$feature];
1876 }
1877
1878 /**
1879  * Allows a theme to de-register its support of a certain feature
1880  *
1881  * Should be called in the theme's functions.php file. Generally would
1882  * be used for child themes to override support from the parent theme.
1883  *
1884  * @since 3.0.0
1885  * @see add_theme_support()
1886  * @param string $feature the feature being added
1887  * @return bool Whether feature was removed.
1888  */
1889 function remove_theme_support( $feature ) {
1890         // Blacklist: for internal registrations not used directly by themes.
1891         if ( in_array( $feature, array( 'custom-background', 'custom-header', 'editor-style', 'widgets', 'menus' ) ) )
1892                 return false;
1893         return _remove_theme_support( $feature );
1894 }
1895
1896 /**
1897  * Do not use. Removes theme support internally, ignorant of the blacklist.
1898  *
1899  * @access private
1900  * @since 3.1.0
1901  */
1902 function _remove_theme_support( $feature ) {
1903         global $_wp_theme_features;
1904
1905         if ( ! isset( $_wp_theme_features[$feature] ) )
1906                 return false;
1907         unset( $_wp_theme_features[$feature] );
1908         return true;
1909 }
1910
1911 /**
1912  * Checks a theme's support for a given feature
1913  *
1914  * @since 2.9.0
1915  * @param string $feature the feature being checked
1916  * @return boolean
1917  */
1918 function current_theme_supports( $feature ) {
1919         global $_wp_theme_features;
1920
1921         if ( !isset( $_wp_theme_features[$feature] ) )
1922                 return false;
1923
1924         // If no args passed then no extra checks need be performed
1925         if ( func_num_args() <= 1 )
1926                 return true;
1927
1928         $args = array_slice( func_get_args(), 1 );
1929
1930         // @todo Allow pluggable arg checking
1931         switch ( $feature ) {
1932                 case 'post-thumbnails':
1933                         // post-thumbnails can be registered for only certain content/post types by passing
1934                         // an array of types to add_theme_support().  If no array was passed, then
1935                         // any type is accepted
1936                         if ( true === $_wp_theme_features[$feature] )  // Registered for all types
1937                                 return true;
1938                         $content_type = $args[0];
1939                         if ( in_array($content_type, $_wp_theme_features[$feature][0]) )
1940                                 return true;
1941                         else
1942                                 return false;
1943                         break;
1944         }
1945
1946         return true;
1947 }
1948
1949 /**
1950  * Checks a theme's support for a given feature before loading the functions which implement it.
1951  *
1952  * @since 2.9.0
1953  * @param string $feature the feature being checked
1954  * @param string $include the file containing the functions that implement the feature
1955  */
1956 function require_if_theme_supports( $feature, $include) {
1957         if ( current_theme_supports( $feature ) )
1958                 require ( $include );
1959 }
1960
1961 /**
1962  * Checks an attachment being deleted to see if it's a header or background image.
1963  *
1964  * If true it removes the theme modification which would be pointing at the deleted
1965  * attachment
1966  *
1967  * @access private
1968  * @since 3.0.0
1969  * @param int $id the attachment id
1970  */
1971 function _delete_attachment_theme_mod( $id ) {
1972         $attachment_image = wp_get_attachment_url( $id );
1973         $header_image = get_header_image();
1974         $background_image = get_background_image();
1975
1976         if ( $header_image && $header_image == $attachment_image )
1977                 remove_theme_mod( 'header_image' );
1978
1979         if ( $background_image && $background_image == $attachment_image )
1980                 remove_theme_mod( 'background_image' );
1981 }
1982
1983 add_action( 'delete_attachment', '_delete_attachment_theme_mod' );
1984
1985 ?>