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