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