WordPress.org

Make WordPress Core

Ticket #15926: theme.php

File theme.php, 49.3 KB (added by firetag, 3 years ago)
Line 
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 **/
16function 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 */
34function 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 */
46function 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 */
61function 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 */
80function 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 */
105function 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 */
127function 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 */
139function 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 */
155function 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 */
183function 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 */
264function 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 */
471function 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 */
488function 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 */
508function 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 */
541function 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 */
567function 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 */
653function 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 */
678function 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 */
713function 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 */
725function 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 */
736function 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 */
747function 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 */
758function 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 */
787function 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 */
815function 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 */
848function 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 */
871function 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 */
887function 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 */
902function 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 */
917function 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 */
952function 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 */
963function 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 */
974function 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 */
997function 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 */
1021function 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 */
1044function 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 */
1079function 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 */
1096function 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 */
1111function 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}
1137add_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 */
1147function _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 */
1159function _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 */
1172function 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 */
1187function 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 */
1216function 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 */
1237function 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 */
1270function 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 */
1289function 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 */
1311function 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 */
1333function 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 */
1347function 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 */
1358function 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 */
1370function 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 */
1381function 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 */
1400function 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 */
1420function 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 */
1438function 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 */
1457function 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 */
1468function background_image() {
1469        echo get_background_image();
1470}
1471
1472/**
1473 * Retrieve background image alt attribute for custom background.
1474 *
1475 * @uses BACKGROUND_ALT
1476 *
1477 * @return string
1478 */
1479function get_background_alt() {
1480        $default = defined('BACKGROUND_ALT') ? BACKGROUND_ALT : '';
1481
1482        return get_theme_mod('background_alt', $default);
1483}
1484
1485/**
1486 * Display background image alt attribute.
1487 *
1488 *
1489 */
1490function background_alt() {
1491        echo get_background_alt();
1492}
1493
1494/**
1495 * Retrieve value for custom background color.
1496 *
1497 * @since 3.0.0
1498 * @uses BACKGROUND_COLOR
1499 *
1500 * @return string
1501 */
1502function get_background_color() {
1503        $default = defined('BACKGROUND_COLOR') ? BACKGROUND_COLOR : '';
1504
1505        return get_theme_mod('background_color', $default);
1506}
1507
1508/**
1509 * Display background color value.
1510 *
1511 * @since 3.0.0
1512 */
1513function background_color() {
1514        echo get_background_color();
1515}
1516
1517/**
1518 * Add callbacks for background image display.
1519 *
1520 * The parameter $header_callback callback will be required to display the
1521 * content for the 'wp_head' action. The parameter $admin_header_callback
1522 * callback will be added to Custom_Background class and that will be added
1523 * to the 'admin_menu' action.
1524 *
1525 * @since 3.0.0
1526 * @uses Custom_Background Sets up for $admin_header_callback for administration panel display.
1527 *
1528 * @param callback $header_callback Call on 'wp_head' action.
1529 * @param callback $admin_header_callback Call on custom background administration screen.
1530 * @param callback $admin_image_div_callback Output a custom background image div on the custom background administration screen. Optional.
1531 */
1532function add_custom_background($header_callback = '', $admin_header_callback = '', $admin_image_div_callback = '') {
1533        if ( isset($GLOBALS['custom_background']) )
1534                return;
1535
1536        if ( empty($header_callback) )
1537                $header_callback = '_custom_background_cb';
1538
1539        add_action('wp_head', $header_callback);
1540
1541        add_theme_support( 'custom-background' );
1542
1543        if ( ! is_admin() )
1544                return;
1545        require_once(ABSPATH . 'wp-admin/custom-background.php');
1546        $GLOBALS['custom_background'] =& new Custom_Background($admin_header_callback, $admin_image_div_callback);
1547        add_action('admin_menu', array(&$GLOBALS['custom_background'], 'init'));
1548}
1549
1550/**
1551 * Default custom background callback.
1552 *
1553 * @since 3.0.0
1554 * @see add_custom_background()
1555 * @access protected
1556 */
1557function _custom_background_cb() {
1558        $background = get_background_image();
1559        $color = get_background_color();
1560        if ( ! $background && ! $color )
1561                return;
1562
1563        $style = $color ? "background-color: #$color;" : '';
1564
1565        if ( $background ) {
1566                $image = " background-image: url('$background');";
1567
1568                $repeat = get_theme_mod( 'background_repeat', 'repeat' );
1569                if ( ! in_array( $repeat, array( 'no-repeat', 'repeat-x', 'repeat-y', 'repeat' ) ) )
1570                        $repeat = 'repeat';
1571                $repeat = " background-repeat: $repeat;";
1572
1573                $position = get_theme_mod( 'background_position_x', 'left' );
1574                if ( ! in_array( $position, array( 'center', 'right', 'left' ) ) )
1575                        $position = 'left';
1576                $position = " background-position: top $position;";
1577
1578                $attachment = get_theme_mod( 'background_attachment', 'scroll' );
1579                if ( ! in_array( $attachment, array( 'fixed', 'scroll' ) ) )
1580                        $attachment = 'scroll';
1581                $attachment = " background-attachment: $attachment;";
1582
1583                $style .= $image . $repeat . $position . $attachment;
1584        }
1585?>
1586<style type="text/css">
1587body { <?php echo trim( $style ); ?> }
1588</style>
1589<?php
1590}
1591
1592/**
1593 * Add callback for custom TinyMCE editor stylesheets.
1594 *
1595 * The parameter $stylesheet is the name of the stylesheet, relative to
1596 * the theme root. It also accepts an array of stylesheets.
1597 * It is optional and defaults to 'editor-style.css'.
1598 *
1599 * @since 3.0.0
1600 *
1601 * @param mixed $stylesheet Optional. Stylesheet name or array thereof, relative to theme root.
1602 *      Defaults to 'editor-style.css'
1603 */
1604function add_editor_style( $stylesheet = 'editor-style.css' ) {
1605
1606        add_theme_support( 'editor-style' );
1607
1608        if ( ! is_admin() )
1609                return;
1610
1611        global $editor_styles;
1612        $editor_styles = (array) $editor_styles;
1613        $stylesheet    = (array) $stylesheet;
1614        if ( is_rtl() ) {
1615                $rtl_stylesheet = str_replace('.css', '-rtl.css', $stylesheet[0]);
1616                $stylesheet[] = $rtl_stylesheet;
1617        }
1618
1619        $editor_styles = array_merge( $editor_styles, $stylesheet );
1620}
1621
1622/**
1623 * Allows a theme to register its support of a certain feature
1624 *
1625 * Must be called in the theme's functions.php file to work.
1626 * If attached to a hook, it must be after_setup_theme.
1627 * The init hook may be too late for some features.
1628 *
1629 * @since 2.9.0
1630 * @param string $feature the feature being added
1631 */
1632function add_theme_support( $feature ) {
1633        global $_wp_theme_features;
1634
1635        if ( func_num_args() == 1 )
1636                $_wp_theme_features[$feature] = true;
1637        else
1638                $_wp_theme_features[$feature] = array_slice( func_get_args(), 1 );
1639}
1640
1641/**
1642 * Allows a theme to de-register its support of a certain feature
1643 *
1644 * Should be called in the theme's functions.php file. Generally would
1645 * be used for child themes to override support from the parent theme.
1646 *
1647 * @since 3.0.0
1648 * @see add_theme_support()
1649 * @param string $feature the feature being added
1650 * @return bool Whether feature was removed.
1651 */
1652function remove_theme_support( $feature ) {
1653        // Blacklist: for internal registrations not used directly by themes.
1654        if ( in_array( $feature, array( 'custom-background', 'custom-header', 'editor-style', 'widgets', 'menus' ) ) )
1655                return false;
1656
1657        global $_wp_theme_features;
1658
1659        if ( ! isset( $_wp_theme_features[$feature] ) )
1660                return false;
1661        unset( $_wp_theme_features[$feature] );
1662        return true;
1663}
1664
1665/**
1666 * Checks a theme's support for a given feature
1667 *
1668 * @since 2.9.0
1669 * @param string $feature the feature being checked
1670 * @return boolean
1671 */
1672function current_theme_supports( $feature ) {
1673        global $_wp_theme_features;
1674
1675        if ( !isset( $_wp_theme_features[$feature] ) )
1676                return false;
1677
1678        // If no args passed then no extra checks need be performed
1679        if ( func_num_args() <= 1 )
1680                return true;
1681
1682        $args = array_slice( func_get_args(), 1 );
1683
1684        // @todo Allow pluggable arg checking
1685        switch ( $feature ) {
1686                case 'post-thumbnails':
1687                        // post-thumbnails can be registered for only certain content/post types by passing
1688                        // an array of types to add_theme_support().  If no array was passed, then
1689                        // any type is accepted
1690                        if ( true === $_wp_theme_features[$feature] )  // Registered for all types
1691                                return true;
1692                        $content_type = $args[0];
1693                        if ( in_array($content_type, $_wp_theme_features[$feature][0]) )
1694                                return true;
1695                        else
1696                                return false;
1697                        break;
1698        }
1699
1700        return true;
1701}
1702
1703/**
1704 * Checks a theme's support for a given feature before loading the functions which implement it.
1705 *
1706 * @since 2.9.0
1707 * @param string $feature the feature being checked
1708 * @param string $include the file containing the functions that implement the feature
1709 */
1710function require_if_theme_supports( $feature, $include) {
1711        if ( current_theme_supports( $feature ) )
1712                require ( $include );
1713}
1714
1715/**
1716 * Checks an attachment being deleted to see if it's a header or background image.
1717 *
1718 * If true it removes the theme modification which would be pointing at the deleted
1719 * attachment
1720 *
1721 * @access private
1722 * @since 3.0.0
1723 * @param int $id the attachment id
1724 */
1725function _delete_attachment_theme_mod( $id ) {
1726        $attachment_image = wp_get_attachment_url( $id );
1727        $header_image = get_header_image();
1728        $background_image = get_background_image();
1729
1730        if ( $header_image && $header_image == $attachment_image )
1731                remove_theme_mod( 'header_image' );
1732
1733        if ( $background_image && $background_image == $attachment_image )
1734                remove_theme_mod( 'background_image' );
1735}
1736
1737add_action( 'delete_attachment', '_delete_attachment_theme_mod' );
1738
1739?>