WordPress.org

Make WordPress Core

Ticket #12167: theme.php

File theme.php, 40.1 KB (added by blepoxp, 4 years ago)

Adds a new line after 714.

Line 
1<?php
2/**
3 * Theme, template, and stylesheet functions.
4 *
5 * @package WordPress
6 * @subpackage Template
7 */
8
9/**
10 * Retrieve name of the current stylesheet.
11 *
12 * The theme name that the administrator has currently set the front end theme
13 * as.
14 *
15 * For all extensive purposes, the template name and the stylesheet name are
16 * going to be the same for most cases.
17 *
18 * @since 1.5.0
19 * @uses apply_filters() Calls 'stylesheet' filter on stylesheet name.
20 *
21 * @return string Stylesheet name.
22 */
23function get_stylesheet() {
24        return apply_filters('stylesheet', get_option('stylesheet'));
25}
26
27/**
28 * Retrieve stylesheet directory path for current theme.
29 *
30 * @since 1.5.0
31 * @uses apply_filters() Calls 'stylesheet_directory' filter on stylesheet directory and theme name.
32 *
33 * @return string Path to current theme directory.
34 */
35function get_stylesheet_directory() {
36        $stylesheet = get_stylesheet();
37        $theme_root = get_theme_root( $stylesheet );
38        $stylesheet_dir = "$theme_root/$stylesheet";
39
40        return apply_filters( 'stylesheet_directory', $stylesheet_dir, $stylesheet, $theme_root );
41}
42
43/**
44 * Retrieve stylesheet directory URI.
45 *
46 * @since 1.5.0
47 *
48 * @return string
49 */
50function get_stylesheet_directory_uri() {
51        $stylesheet = get_stylesheet();
52        $theme_root_uri = get_theme_root_uri( $stylesheet );
53        $stylesheet_dir_uri = "$theme_root_uri/$stylesheet";
54
55        return apply_filters( 'stylesheet_directory_uri', $stylesheet_dir_uri, $stylesheet, $theme_root_uri );
56}
57
58/**
59 * Retrieve URI of current theme stylesheet.
60 *
61 * The stylesheet file name is 'style.css' which is appended to {@link
62 * get_stylesheet_directory_uri() stylesheet directory URI} path.
63 *
64 * @since 1.5.0
65 * @uses apply_filters() Calls 'stylesheet_uri' filter on stylesheet URI path and stylesheet directory URI.
66 *
67 * @return string
68 */
69function get_stylesheet_uri() {
70        $stylesheet_dir_uri = get_stylesheet_directory_uri();
71        $stylesheet_uri = $stylesheet_dir_uri . "/style.css";
72        return apply_filters('stylesheet_uri', $stylesheet_uri, $stylesheet_dir_uri);
73}
74
75/**
76 * Retrieve localized stylesheet URI.
77 *
78 * The stylesheet directory for the localized stylesheet files are located, by
79 * default, in the base theme directory. The name of the locale file will be the
80 * locale followed by '.css'. If that does not exist, then the text direction
81 * stylesheet will be checked for existence, for example 'ltr.css'.
82 *
83 * The theme may change the location of the stylesheet directory by either using
84 * the 'stylesheet_directory_uri' filter or the 'locale_stylesheet_uri' filter.
85 * If you want to change the location of the stylesheet files for the entire
86 * WordPress workflow, then change the former. If you just have the locale in a
87 * separate folder, then change the latter.
88 *
89 * @since 2.1.0
90 * @uses apply_filters() Calls 'locale_stylesheet_uri' filter on stylesheet URI path and stylesheet directory URI.
91 *
92 * @return string
93 */
94function get_locale_stylesheet_uri() {
95        global $wp_locale;
96        $stylesheet_dir_uri = get_stylesheet_directory_uri();
97        $dir = get_stylesheet_directory();
98        $locale = get_locale();
99        if ( file_exists("$dir/$locale.css") )
100                $stylesheet_uri = "$stylesheet_dir_uri/$locale.css";
101        elseif ( !empty($wp_locale->text_direction) && file_exists("$dir/{$wp_locale->text_direction}.css") )
102                $stylesheet_uri = "$stylesheet_dir_uri/{$wp_locale->text_direction}.css";
103        else
104                $stylesheet_uri = '';
105        return apply_filters('locale_stylesheet_uri', $stylesheet_uri, $stylesheet_dir_uri);
106}
107
108/**
109 * Retrieve name of the current theme.
110 *
111 * @since 1.5.0
112 * @uses apply_filters() Calls 'template' filter on template option.
113 *
114 * @return string Template name.
115 */
116function get_template() {
117        return apply_filters('template', get_option('template'));
118}
119
120/**
121 * Retrieve current theme directory.
122 *
123 * @since 1.5.0
124 * @uses apply_filters() Calls 'template_directory' filter on template directory path and template name.
125 *
126 * @return string Template directory path.
127 */
128function get_template_directory() {
129        $template = get_template();
130        $theme_root = get_theme_root( $template );
131        $template_dir = "$theme_root/$template";
132
133        return apply_filters( 'template_directory', $template_dir, $template, $theme_root );
134}
135
136/**
137 * Retrieve theme directory URI.
138 *
139 * @since 1.5.0
140 * @uses apply_filters() Calls 'template_directory_uri' filter on template directory URI path and template name.
141 *
142 * @return string Template directory URI.
143 */
144function get_template_directory_uri() {
145        $template = get_template();
146        $theme_root_uri = get_theme_root_uri( $template );
147        $template_dir_uri = "$theme_root_uri/$template";
148
149        return apply_filters( 'template_directory_uri', $template_dir_uri, $template, $theme_root_uri );
150}
151
152/**
153 * Retrieve theme data from parsed theme file.
154 *
155 * The description will have the tags filtered with the following HTML elements
156 * whitelisted. The <b>'a'</b> element with the <em>href</em> and <em>title</em>
157 * attributes. The <b>abbr</b> element with the <em>title</em> attribute. The
158 * <b>acronym<b> element with the <em>title</em> attribute allowed. The
159 * <b>code</b>, <b>em</b>, and <b>strong</b> elements also allowed.
160 *
161 * The style.css file must contain theme name, theme URI, and description. The
162 * data can also contain author URI, author, template (parent template),
163 * version, status, and finally tags. Some of these are not used by WordPress
164 * administration panels, but are used by theme directory web sites which list
165 * the theme.
166 *
167 * @since 1.5.0
168 *
169 * @param string $theme_file Theme file path.
170 * @return array Theme data.
171 */
172function get_theme_data( $theme_file ) {
173        $default_headers = array(
174                'Name' => 'Theme Name',
175                'URI' => 'Theme URI',
176                'Description' => 'Description',
177                'Author' => 'Author',
178                'AuthorURI' => 'Author URI',
179                'Version' => 'Version',
180                'Template' => 'Template',
181                'Status' => 'Status',
182                'Tags' => 'Tags'
183                );
184
185        $themes_allowed_tags = array(
186                'a' => array(
187                        'href' => array(),'title' => array()
188                        ),
189                'abbr' => array(
190                        'title' => array()
191                        ),
192                'acronym' => array(
193                        'title' => array()
194                        ),
195                'code' => array(),
196                'em' => array(),
197                'strong' => array()
198        );
199
200        $theme_data = get_file_data( $theme_file, $default_headers, 'theme' );
201
202        $theme_data['Name'] = $theme_data['Title'] = wp_kses( $theme_data['Name'], $themes_allowed_tags );
203
204        $theme_data['URI'] = esc_url( $theme_data['URI'] );
205
206        $theme_data['Description'] = wptexturize( wp_kses( $theme_data['Description'], $themes_allowed_tags ) );
207
208        $theme_data['AuthorURI'] = esc_url( $theme_data['AuthorURI'] );
209
210        $theme_data['Template'] = wp_kses( $theme_data['Template'], $themes_allowed_tags );
211
212        $theme_data['Version'] = wp_kses( $theme_data['Version'], $themes_allowed_tags );
213
214        if ( $theme_data['Status'] == '' )
215                $theme_data['Status'] = 'publish';
216        else
217                $theme_data['Status'] = wp_kses( $theme_data['Status'], $themes_allowed_tags );
218
219        if ( $theme_data['Tags'] == '' )
220                $theme_data['Tags'] = array();
221        else
222                $theme_data['Tags'] = array_map( 'trim', explode( ',', wp_kses( $theme_data['Tags'], array() ) ) );
223
224        if ( $theme_data['Author'] == '' ) {
225                $theme_data['Author'] = __('Anonymous');
226        } else {
227                if ( empty( $theme_data['AuthorURI'] ) ) {
228                        $theme_data['Author'] = wp_kses( $theme_data['Author'], $themes_allowed_tags );
229                } else {
230                        $theme_data['Author'] = sprintf( '<a href="%1$s" title="%2$s">%3$s</a>', $theme_data['AuthorURI'], __( 'Visit author homepage' ), wp_kses( $theme_data['Author'], $themes_allowed_tags ) );
231                }
232        }
233
234        return $theme_data;
235}
236
237/**
238 * Retrieve list of themes with theme data in theme directory.
239 *
240 * The theme is broken, if it doesn't have a parent theme and is missing either
241 * style.css and, or index.php. If the theme has a parent theme then it is
242 * broken, if it is missing style.css; index.php is optional. The broken theme
243 * list is saved in the {@link $wp_broken_themes} global, which is displayed on
244 * the theme list in the administration panels.
245 *
246 * @since 1.5.0
247 * @global array $wp_broken_themes Stores the broken themes.
248 * @global array $wp_themes Stores the working themes.
249 *
250 * @return array Theme list with theme data.
251 */
252function get_themes() {
253        global $wp_themes, $wp_broken_themes;
254
255        if ( isset($wp_themes) )
256                return $wp_themes;
257
258        /* Register the default root as a theme directory */
259        register_theme_directory( get_theme_root() );
260
261        if ( !$theme_files = search_theme_directories() )
262                return false;
263
264        asort( $theme_files );
265
266        $wp_themes = array();
267
268        foreach ( (array) $theme_files as $theme_file ) {
269                $theme_root = $theme_file['theme_root'];
270                $theme_file = $theme_file['theme_file'];
271
272                if ( !is_readable("$theme_root/$theme_file") ) {
273                        $wp_broken_themes[$theme_file] = array('Name' => $theme_file, 'Title' => $theme_file, 'Description' => __('File not readable.'));
274                        continue;
275                }
276
277                $theme_data = get_theme_data("$theme_root/$theme_file");
278
279                $name        = $theme_data['Name'];
280                $title       = $theme_data['Title'];
281                $description = wptexturize($theme_data['Description']);
282                $version     = $theme_data['Version'];
283                $author      = $theme_data['Author'];
284                $template    = $theme_data['Template'];
285                $stylesheet  = dirname($theme_file);
286
287                $screenshot = false;
288                foreach ( array('png', 'gif', 'jpg', 'jpeg') as $ext ) {
289                        if (file_exists("$theme_root/$stylesheet/screenshot.$ext")) {
290                                $screenshot = "screenshot.$ext";
291                                break;
292                        }
293                }
294
295                if ( empty($name) ) {
296                        $name = dirname($theme_file);
297                        $title = $name;
298                }
299
300                if ( empty($template) ) {
301                        if ( file_exists("$theme_root/$stylesheet/index.php") )
302                                $template = $stylesheet;
303                        else
304                                continue;
305                }
306
307                $template = trim( $template );
308
309                if ( !file_exists("$theme_root/$template/index.php") ) {
310                        $parent_dir = dirname(dirname($theme_file));
311                        if ( file_exists("$theme_root/$parent_dir/$template/index.php") ) {
312                                $template = "$parent_dir/$template";
313                                $template_directory = "$theme_root/$template";
314                        } else {
315                                /**
316                                 * The parent theme doesn't exist in the current theme's folder or sub folder
317                                 * so lets use the theme root for the parent template.
318                                 */
319                                if ( isset($theme_files[$template]) && file_exists( $theme_files[$template]['theme_root'] . "/$template/index.php" ) ) {
320                                        $template_directory = $theme_files[$template]['theme_root'] . "/$template";
321                                } else {
322                                        $wp_broken_themes[$name] = array('Name' => $name, 'Title' => $title, 'Description' => __('Template is missing.'));
323                                        continue;
324                                }
325
326                        }
327                } else {
328                        $template_directory = trim( $theme_root . '/' . $template );
329                }
330
331                $stylesheet_files = array();
332                $template_files = array();
333
334                $stylesheet_dir = @ dir("$theme_root/$stylesheet");
335                if ( $stylesheet_dir ) {
336                        while ( ($file = $stylesheet_dir->read()) !== false ) {
337                                if ( !preg_match('|^\.+$|', $file) ) {
338                                        if ( preg_match('|\.css$|', $file) )
339                                                $stylesheet_files[] = "$theme_root/$stylesheet/$file";
340                                        elseif ( preg_match('|\.php$|', $file) )
341                                                $template_files[] = "$theme_root/$stylesheet/$file";
342                                }
343                        }
344                        @ $stylesheet_dir->close();
345                }
346
347                $template_dir = @ dir("$template_directory");
348                if ( $template_dir ) {
349                        while ( ($file = $template_dir->read()) !== false ) {
350                                if ( preg_match('|^\.+$|', $file) )
351                                        continue;
352                                if ( preg_match('|\.php$|', $file) ) {
353                                        $template_files[] = "$template_directory/$file";
354                                } elseif ( is_dir("$template_directory/$file") ) {
355                                        $template_subdir = @ dir("$template_directory/$file");
356                                        if ( !$template_subdir )
357                                                continue;
358                                        while ( ($subfile = $template_subdir->read()) !== false ) {
359                                                if ( preg_match('|^\.+$|', $subfile) )
360                                                        continue;
361                                                if ( preg_match('|\.php$|', $subfile) )
362                                                        $template_files[] = "$template_directory/$file/$subfile";
363                                        }
364                                        @ $template_subdir->close();
365                                }
366                        }
367                        @ $template_dir->close();
368                }
369
370                //Make unique and remove duplicates when stylesheet and template are the same i.e. most themes
371                $template_files = array_unique($template_files);
372                $stylesheet_files = array_unique($stylesheet_files);
373
374                $template_dir = dirname($template_files[0]);
375                $stylesheet_dir = dirname($stylesheet_files[0]);
376
377                if ( empty($template_dir) )
378                        $template_dir = '/';
379                if ( empty($stylesheet_dir) )
380                        $stylesheet_dir = '/';
381
382                // Check for theme name collision.  This occurs if a theme is copied to
383                // a new theme directory and the theme header is not updated.  Whichever
384                // theme is first keeps the name.  Subsequent themes get a suffix applied.
385                // The Default and Classic themes always trump their pretenders.
386                if ( isset($wp_themes[$name]) ) {
387                        if ( ('WordPress Default' == $name || 'WordPress Classic' == $name) &&
388                                         ('default' == $stylesheet || 'classic' == $stylesheet) ) {
389                                // If another theme has claimed to be one of our default themes, move
390                                // them aside.
391                                $suffix = $wp_themes[$name]['Stylesheet'];
392                                $new_name = "$name/$suffix";
393                                $wp_themes[$new_name] = $wp_themes[$name];
394                                $wp_themes[$new_name]['Name'] = $new_name;
395                        } else {
396                                $name = "$name/$stylesheet";
397                        }
398                }
399
400                $theme_roots[$stylesheet] = str_replace( WP_CONTENT_DIR, '', $theme_root );
401                $wp_themes[$name] = array( 'Name' => $name, 'Title' => $title, 'Description' => $description, 'Author' => $author, 'Version' => $version, 'Template' => $template, 'Stylesheet' => $stylesheet, 'Template Files' => $template_files, 'Stylesheet Files' => $stylesheet_files, 'Template Dir' => $template_dir, 'Stylesheet Dir' => $stylesheet_dir, 'Status' => $theme_data['Status'], 'Screenshot' => $screenshot, 'Tags' => $theme_data['Tags'], 'Theme Root' => $theme_root, 'Theme Root URI' => str_replace( WP_CONTENT_DIR, content_url(), $theme_root ) );
402        }
403
404        unset($theme_files);
405
406        /* Store theme roots in the DB */
407        if ( get_site_transient( 'theme_roots' ) != $theme_roots )
408                set_site_transient( 'theme_roots', $theme_roots, 7200 ); // cache for two hours
409        unset($theme_roots);
410
411        /* Resolve theme dependencies. */
412        $theme_names = array_keys( $wp_themes );
413        foreach ( (array) $theme_names as $theme_name ) {
414                $wp_themes[$theme_name]['Parent Theme'] = '';
415                if ( $wp_themes[$theme_name]['Stylesheet'] != $wp_themes[$theme_name]['Template'] ) {
416                        foreach ( (array) $theme_names as $parent_theme_name ) {
417                                if ( ($wp_themes[$parent_theme_name]['Stylesheet'] == $wp_themes[$parent_theme_name]['Template']) && ($wp_themes[$parent_theme_name]['Template'] == $wp_themes[$theme_name]['Template']) ) {
418                                        $wp_themes[$theme_name]['Parent Theme'] = $wp_themes[$parent_theme_name]['Name'];
419                                        break;
420                                }
421                        }
422                }
423        }
424
425        return $wp_themes;
426}
427
428/**
429 * Retrieve theme roots.
430 *
431 * @since 2.9.0
432 *
433 * @return array Theme roots
434 */
435function get_theme_roots() {
436        $theme_roots = get_site_transient( 'theme_roots' );
437        if ( false === $theme_roots ) {
438                get_themes();
439                $theme_roots = get_site_transient( 'theme_roots' ); // this is set in get_theme()
440        }
441        return $theme_roots;
442}
443
444/**
445 * Retrieve theme data.
446 *
447 * @since 1.5.0
448 *
449 * @param string $theme Theme name.
450 * @return array|null Null, if theme name does not exist. Theme data, if exists.
451 */
452function get_theme($theme) {
453        $themes = get_themes();
454
455        if ( array_key_exists($theme, $themes) )
456                return $themes[$theme];
457
458        return null;
459}
460
461/**
462 * Retrieve current theme display name.
463 *
464 * If the 'current_theme' option has already been set, then it will be returned
465 * instead. If it is not set, then each theme will be iterated over until both
466 * the current stylesheet and current template name.
467 *
468 * @since 1.5.0
469 *
470 * @return string
471 */
472function get_current_theme() {
473        if ( $theme = get_option('current_theme') )
474                return $theme;
475
476        $themes = get_themes();
477        $theme_names = array_keys($themes);
478        $current_template = get_option('template');
479        $current_stylesheet = get_option('stylesheet');
480        $current_theme = 'WordPress Default';
481
482        if ( $themes ) {
483                foreach ( (array) $theme_names as $theme_name ) {
484                        if ( $themes[$theme_name]['Stylesheet'] == $current_stylesheet &&
485                                        $themes[$theme_name]['Template'] == $current_template ) {
486                                $current_theme = $themes[$theme_name]['Name'];
487                                break;
488                        }
489                }
490        }
491
492        update_option('current_theme', $current_theme);
493
494        return $current_theme;
495}
496
497/**
498 * Register a directory that contains themes.
499 *
500 * @since 2.9.0
501 *
502 * @param string $directory Either the full filesystem path to a theme folder or a folder within WP_CONTENT_DIR
503 * @return bool
504 */
505function register_theme_directory( $directory) {
506        global $wp_theme_directories;
507
508        /* If this folder does not exist, return and do not register */
509        if ( !file_exists( $directory ) )
510                        /* Try prepending as the theme directory could be relative to the content directory */
511                $registered_directory = WP_CONTENT_DIR . '/' . $directory;
512        else
513                $registered_directory = $directory;
514
515        /* If this folder does not exist, return and do not register */
516        if ( !file_exists( $registered_directory ) )
517                return false;
518
519        $wp_theme_directories[] = $registered_directory;
520
521        return true;
522}
523
524/**
525 * Search all registered theme directories for complete and valid themes.
526 *
527 * @since 2.9.0
528 *
529 * @return array Valid themes found
530 */
531function search_theme_directories() {
532        global $wp_theme_directories, $wp_broken_themes;
533        if ( empty( $wp_theme_directories ) )
534                return false;
535
536        $theme_files = array();
537        $wp_broken_themes = array();
538
539        /* Loop the registered theme directories and extract all themes */
540        foreach ( (array) $wp_theme_directories as $theme_root ) {
541                $theme_loc = $theme_root;
542
543                /* We don't want to replace all forward slashes, see Trac #4541 */
544                if ( '/' != WP_CONTENT_DIR )
545                        $theme_loc = str_replace(WP_CONTENT_DIR, '', $theme_root);
546
547                /* Files in the root of the current theme directory and one subdir down */
548                $themes_dir = @ opendir($theme_root);
549
550                if ( !$themes_dir )
551                        return false;
552
553                while ( ($theme_dir = readdir($themes_dir)) !== false ) {
554                        if ( is_dir($theme_root . '/' . $theme_dir) && is_readable($theme_root . '/' . $theme_dir) ) {
555                                if ( $theme_dir{0} == '.' || $theme_dir == 'CVS' )
556                                        continue;
557
558                                $stylish_dir = @opendir($theme_root . '/' . $theme_dir);
559                                $found_stylesheet = false;
560
561                                while ( ($theme_file = readdir($stylish_dir)) !== false ) {
562                                        if ( $theme_file == 'style.css' ) {
563                                                $theme_files[$theme_dir] = array( 'theme_file' => $theme_dir . '/' . $theme_file, 'theme_root' => $theme_root );
564                                                $found_stylesheet = true;
565                                                break;
566                                        }
567                                }
568                                @closedir($stylish_dir);
569
570                                if ( !$found_stylesheet ) { // look for themes in that dir
571                                        $subdir = "$theme_root/$theme_dir";
572                                        $subdir_name = $theme_dir;
573                                        $theme_subdirs = @opendir( $subdir );
574
575                                        $found_subdir_themes = false;
576                                        while ( ($theme_subdir = readdir($theme_subdirs)) !== false ) {
577                                                if ( is_dir( $subdir . '/' . $theme_subdir) && is_readable($subdir . '/' . $theme_subdir) ) {
578                                                        if ( $theme_subdir{0} == '.' || $theme_subdir == 'CVS' )
579                                                                continue;
580
581                                                        $stylish_dir = @opendir($subdir . '/' . $theme_subdir);
582                                                        $found_stylesheet = false;
583
584                                                        while ( ($theme_file = readdir($stylish_dir)) !== false ) {
585                                                                if ( $theme_file == 'style.css' ) {
586                                                                        $theme_files["$theme_dir/$theme_subdir"] = array( 'theme_file' => $subdir_name . '/' . $theme_subdir . '/' . $theme_file, 'theme_root' => $theme_root );
587                                                                        $found_stylesheet = true;
588                                                                        $found_subdir_themes = true;
589                                                                        break;
590                                                                }
591                                                        }
592                                                        @closedir($stylish_dir);
593                                                }
594                                        }
595                                        @closedir($theme_subdir);
596                                        if ( !$found_subdir_themes )
597                                                $wp_broken_themes[$theme_dir] = array('Name' => $theme_dir, 'Title' => $theme_dir, 'Description' => __('Stylesheet is missing.'));
598                                }
599                        }
600                }
601                if ( is_dir( $theme_dir ) )
602                        @closedir( $theme_dir );
603        }
604        return $theme_files;
605}
606
607/**
608 * Retrieve path to themes directory.
609 *
610 * Does not have trailing slash.
611 *
612 * @since 1.5.0
613 * @param $stylesheet_or_template The stylesheet or template name of the theme
614 * @uses apply_filters() Calls 'theme_root' filter on path.
615 *
616 * @return string Theme path.
617 */
618function get_theme_root( $stylesheet_or_template = false ) {
619        if ($stylesheet_or_template) {
620                $theme_roots = get_theme_roots();
621
622                if ( $theme_roots[$stylesheet_or_template] )
623                        $theme_root = WP_CONTENT_DIR . $theme_roots[$stylesheet_or_template];
624                else
625                        $theme_root = WP_CONTENT_DIR . '/themes';
626        } else {
627                $theme_root = WP_CONTENT_DIR . '/themes';
628        }
629
630        return apply_filters( 'theme_root', $theme_root );
631}
632
633/**
634 * Retrieve URI for themes directory.
635 *
636 * Does not have trailing slash.
637 *
638 * @since 1.5.0
639 * @param $stylesheet_or_template The stylesheet or template name of the theme
640 *
641 * @return string Themes URI.
642 */
643function get_theme_root_uri( $stylesheet_or_template = false ) {
644        $theme_roots = get_theme_roots();
645
646        if ( $theme_roots[$stylesheet_or_template] )
647                $theme_root_uri = content_url( $theme_roots[$stylesheet_or_template] );
648        else
649                $theme_root_uri = content_url( 'themes' );
650
651        return apply_filters( 'theme_root_uri', $theme_root_uri, get_option('siteurl'), $stylesheet_or_template );
652}
653
654/**
655 * Retrieve path to file without the use of extension.
656 *
657 * Used to quickly retrieve the path of file without including the file
658 * extension. It will also check the parent template, if the file exists, with
659 * the use of {@link locate_template()}. Allows for more generic file location
660 * without the use of the other get_*_template() functions.
661 *
662 * Can be used with include() or require() to retrieve path.
663 * <code>
664 * if( '' != get_query_template( '404' ) )
665 *     include( get_query_template( '404' ) );
666 * </code>
667 * or the same can be accomplished with
668 * <code>
669 * if( '' != get_404_template() )
670 *     include( get_404_template() );
671 * </code>
672 *
673 * @since 1.5.0
674 *
675 * @param string $type Filename without extension.
676 * @return string Full path to file.
677 */
678function get_query_template($type) {
679        $type = preg_replace( '|[^a-z0-9-]+|', '', $type );
680        return apply_filters("{$type}_template", locate_template(array("{$type}.php")));
681}
682
683/**
684 * Retrieve path of 404 template in current or parent template.
685 *
686 * @since 1.5.0
687 *
688 * @return string
689 */
690function get_404_template() {
691        return get_query_template('404');
692}
693
694/**
695 * Retrieve path of archive template in current or parent template.
696 *
697 * @since 1.5.0
698 *
699 * @return string
700 */
701function get_archive_template() {
702        return get_query_template('archive');
703}
704
705/**
706 * Retrieve path of author template in current or parent template.
707 *
708 * @since 1.5.0
709 *
710 * @return string
711 */
712function get_author_template() {
713        $author_id = absint( get_query_var( 'author' ) );
714        $author = get_user_by( 'id', $author_id );
715        $author = $author->user_nicename;
716
717        $templates = array();
718
719        if ( $author )
720                $templates[] = "author-{$author}.php";
721        if ( $author_id )
722                $templates[] = "author-{$author_id}.php";
723        $templates[] = 'author.php';
724
725        $template = locate_template( $templates );
726        return apply_filters( 'author_template', $template );
727}
728
729/**
730 * Retrieve path of category template in current or parent template.
731 *
732 * Works by first retrieving the current slug for example 'category-default.php' and then
733 * trying category ID, for example 'category-1.php' and will finally fallback to category.php
734 * template, if those files don't exist.
735 *
736 * @since 1.5.0
737 * @uses apply_filters() Calls 'category_template' on file path of category template.
738 *
739 * @return string
740 */
741function get_category_template() {
742        $cat_ID = absint( get_query_var('cat') );
743        $category = get_category( $cat_ID );
744
745        $templates = array();
746
747        if ( !is_wp_error($category) )
748                $templates[] = "category-{$category->slug}.php";
749
750        $templates[] = "category-$cat_ID.php";
751        $templates[] = "category.php";
752
753        $template = locate_template($templates);
754        return apply_filters('category_template', $template);
755}
756
757/**
758 * Retrieve path of tag template in current or parent template.
759 *
760 * Works by first retrieving the current tag name, for example 'tag-wordpress.php' and then
761 * trying tag ID, for example 'tag-1.php' and will finally fallback to tag.php
762 * template, if those files don't exist.
763 *
764 * @since 2.3.0
765 * @uses apply_filters() Calls 'tag_template' on file path of tag template.
766 *
767 * @return string
768 */
769function get_tag_template() {
770        $tag_id = absint( get_query_var('tag_id') );
771        $tag_name = get_query_var('tag');
772
773        $templates = array();
774
775        if ( $tag_name )
776                $templates[] = "tag-$tag_name.php";
777        if ( $tag_id )
778                $templates[] = "tag-$tag_id.php";
779        $templates[] = "tag.php";
780
781        $template = locate_template($templates);
782        return apply_filters('tag_template', $template);
783}
784
785/**
786 * Retrieve path of taxonomy template in current or parent template.
787 *
788 * Retrieves the taxonomy and term, if term is available. The template is
789 * prepended with 'taxonomy-' and followed by both the taxonomy string and
790 * the taxonomy string followed by a dash and then followed by the term.
791 *
792 * The taxonomy and term template is checked and used first, if it exists.
793 * Second, just the taxonomy template is checked, and then finally, taxonomy.php
794 * template is used. If none of the files exist, then it will fall back on to
795 * index.php.
796 *
797 * @since unknown (2.6.0 most likely)
798 * @uses apply_filters() Calls 'taxonomy_template' filter on found path.
799 *
800 * @return string
801 */
802function get_taxonomy_template() {
803        $taxonomy = get_query_var('taxonomy');
804        $term = get_query_var('term');
805
806        $templates = array();
807        if ( $taxonomy && $term )
808                $templates[] = "taxonomy-$taxonomy-$term.php";
809        if ( $taxonomy )
810                $templates[] = "taxonomy-$taxonomy.php";
811
812        $templates[] = "taxonomy.php";
813
814        $template = locate_template($templates);
815        return apply_filters('taxonomy_template', $template);
816}
817
818/**
819 * Retrieve path of date template in current or parent template.
820 *
821 * @since 1.5.0
822 *
823 * @return string
824 */
825function get_date_template() {
826        return get_query_template('date');
827}
828
829/**
830 * Retrieve path of home template in current or parent template.
831 *
832 * Attempts to locate 'home.php' first before falling back to 'index.php'.
833 *
834 * @since 1.5.0
835 * @uses apply_filters() Calls 'home_template' on file path of home template.
836 *
837 * @return string
838 */
839function get_home_template() {
840        $template = locate_template(array('home.php', 'index.php'));
841        return apply_filters('home_template', $template);
842}
843
844/**
845 * Retrieve path of page template in current or parent template.
846 *
847 * Will first look for the specifically assigned page template
848 * The will search for 'page-{slug}.php' followed by 'page-id.php'
849 * and finally 'page.php'
850 *
851 * @since 1.5.0
852 *
853 * @return string
854 */
855function get_page_template() {
856        global $wp_query;
857
858        $id = (int) $wp_query->post->ID;
859        $template = get_post_meta($id, '_wp_page_template', true);
860        $pagename = get_query_var('pagename');
861
862        if ( 'default' == $template )
863                $template = '';
864
865        $templates = array();
866        if ( !empty($template) && !validate_file($template) )
867                $templates[] = $template;
868        if ( $pagename )
869                $templates[] = "page-$pagename.php";
870        if ( $id )
871                $templates[] = "page-$id.php";
872        $templates[] = "page.php";
873
874        return apply_filters('page_template', locate_template($templates));
875}
876
877/**
878 * Retrieve path of paged template in current or parent template.
879 *
880 * @since 1.5.0
881 *
882 * @return string
883 */
884function get_paged_template() {
885        return get_query_template('paged');
886}
887
888/**
889 * Retrieve path of search template in current or parent template.
890 *
891 * @since 1.5.0
892 *
893 * @return string
894 */
895function get_search_template() {
896        return get_query_template('search');
897}
898
899/**
900 * Retrieve path of single template in current or parent template.
901 *
902 * @since 1.5.0
903 *
904 * @return string
905 */
906function get_single_template() {
907        return get_query_template('single');
908}
909
910/**
911 * Retrieve path of attachment template in current or parent template.
912 *
913 * The attachment path first checks if the first part of the mime type exists.
914 * The second check is for the second part of the mime type. The last check is
915 * for both types separated by an underscore. If neither are found then the file
916 * 'attachment.php' is checked and returned.
917 *
918 * Some examples for the 'text/plain' mime type are 'text.php', 'plain.php', and
919 * finally 'text_plain.php'.
920 *
921 * @since 2.0.0
922 *
923 * @return string
924 */
925function get_attachment_template() {
926        global $posts;
927        $type = explode('/', $posts[0]->post_mime_type);
928        if ( $template = get_query_template($type[0]) )
929                return $template;
930        elseif ( $template = get_query_template($type[1]) )
931                return $template;
932        elseif ( $template = get_query_template("$type[0]_$type[1]") )
933                return $template;
934        else
935                return get_query_template('attachment');
936}
937
938/**
939 * Retrieve path of comment popup template in current or parent template.
940 *
941 * Checks for comment popup template in current template, if it exists or in the
942 * parent template. If it doesn't exist, then it retrieves the comment-popup.php
943 * file from the default theme. The default theme must then exist for it to
944 * work.
945 *
946 * @since 1.5.0
947 * @uses apply_filters() Calls 'comments_popup_template' filter on path.
948 *
949 * @return string
950 */
951function get_comments_popup_template() {
952        $template = locate_template(array("comments-popup.php"));
953        if ('' == $template)
954                $template = get_theme_root() . '/default/comments-popup.php';
955
956        return apply_filters('comments_popup_template', $template);
957}
958
959/**
960 * Retrieve the name of the highest priority template file that exists.
961 *
962 * Searches in the STYLESHEETPATH before TEMPLATEPATH so that themes which
963 * inherit from a parent theme can just overload one file.
964 *
965 * @since 2.7.0
966 *
967 * @param array $template_names Array of template files to search for in priority order.
968 * @param bool $load If true the template file will be loaded if it is found.
969 * @return string The template filename if one is located.
970 */
971function locate_template($template_names, $load = false) {
972        if (!is_array($template_names))
973                return '';
974
975        $located = '';
976        foreach($template_names as $template_name) {
977                if ( file_exists(STYLESHEETPATH . '/' . $template_name)) {
978                        $located = STYLESHEETPATH . '/' . $template_name;
979                        break;
980                } else if ( file_exists(TEMPLATEPATH . '/' . $template_name) ) {
981                        $located = TEMPLATEPATH . '/' . $template_name;
982                        break;
983                }
984        }
985
986        if ($load && '' != $located)
987                load_template($located);
988
989        return $located;
990}
991
992/**
993 * Require once the template file with WordPress environment.
994 *
995 * The globals are set up for the template file to ensure that the WordPress
996 * environment is available from within the function. The query variables are
997 * also available.
998 *
999 * @since 1.5.0
1000 *
1001 * @param string $_template_file Path to template file.
1002 */
1003function load_template($_template_file) {
1004        global $posts, $post, $wp_did_header, $wp_did_template_redirect, $wp_query, $wp_rewrite, $wpdb, $wp_version, $wp, $id, $comment, $user_ID;
1005
1006        if ( is_array($wp_query->query_vars) )
1007                extract($wp_query->query_vars, EXTR_SKIP);
1008
1009        require_once($_template_file);
1010}
1011
1012/**
1013 * Display localized stylesheet link element.
1014 *
1015 * @since 2.1.0
1016 */
1017function locale_stylesheet() {
1018        $stylesheet = get_locale_stylesheet_uri();
1019        if ( empty($stylesheet) )
1020                return;
1021        echo '<link rel="stylesheet" href="' . $stylesheet . '" type="text/css" media="screen" />';
1022}
1023
1024/**
1025 * Start preview theme output buffer.
1026 *
1027 * Will only preform task if the user has permissions and template and preview
1028 * query variables exist.
1029 *
1030 * @since 2.6.0
1031 */
1032function preview_theme() {
1033        if ( ! (isset($_GET['template']) && isset($_GET['preview'])) )
1034                return;
1035
1036        if ( !current_user_can( 'switch_themes' ) )
1037                return;
1038
1039        $_GET['template'] = preg_replace('|[^a-z0-9_./-]|i', '', $_GET['template']);
1040
1041        if ( validate_file($_GET['template']) )
1042                return;
1043
1044        add_filter( 'template', '_preview_theme_template_filter' );
1045
1046        if ( isset($_GET['stylesheet']) ) {
1047                $_GET['stylesheet'] = preg_replace('|[^a-z0-9_./-]|i', '', $_GET['stylesheet']);
1048                if ( validate_file($_GET['stylesheet']) )
1049                        return;
1050                add_filter( 'stylesheet', '_preview_theme_stylesheet_filter' );
1051        }
1052
1053        // Prevent theme mods to current theme being used on theme being previewed
1054        add_filter( 'pre_option_mods_' . get_current_theme(), create_function( '', "return array();" ) );
1055
1056        ob_start( 'preview_theme_ob_filter' );
1057}
1058add_action('setup_theme', 'preview_theme');
1059
1060/**
1061 * Private function to modify the current template when previewing a theme
1062 *
1063 * @since 2.9.0
1064 * @access private
1065 *
1066 * @return string
1067 */
1068function _preview_theme_template_filter() {
1069        return isset($_GET['template']) ? $_GET['template'] : '';
1070}
1071
1072/**
1073 * Private function to modify the current stylesheet when previewing a theme
1074 *
1075 * @since 2.9.0
1076 * @access private
1077 *
1078 * @return string
1079 */
1080function _preview_theme_stylesheet_filter() {
1081        return isset($_GET['stylesheet']) ? $_GET['stylesheet'] : '';
1082}
1083
1084/**
1085 * Callback function for ob_start() to capture all links in the theme.
1086 *
1087 * @since 2.6.0
1088 * @access private
1089 *
1090 * @param string $content
1091 * @return string
1092 */
1093function preview_theme_ob_filter( $content ) {
1094        return preg_replace_callback( "|(<a.*?href=([\"']))(.*?)([\"'].*?>)|", 'preview_theme_ob_filter_callback', $content );
1095}
1096
1097/**
1098 * Manipulates preview theme links in order to control and maintain location.
1099 *
1100 * Callback function for preg_replace_callback() to accept and filter matches.
1101 *
1102 * @since 2.6.0
1103 * @access private
1104 *
1105 * @param array $matches
1106 * @return string
1107 */
1108function preview_theme_ob_filter_callback( $matches ) {
1109        if ( strpos($matches[4], 'onclick') !== false )
1110                $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.
1111        if (
1112                ( false !== strpos($matches[3], '/wp-admin/') )
1113        ||
1114                ( false !== strpos( $matches[3], '://' ) && 0 !== strpos( $matches[3], home_url() ) )
1115        ||
1116                ( false !== strpos($matches[3], '/feed/') )
1117        ||
1118                ( false !== strpos($matches[3], '/trackback/') )
1119        )
1120                return $matches[1] . "#$matches[2] onclick=$matches[2]return false;" . $matches[4];
1121
1122        $link = add_query_arg( array('preview' => 1, 'template' => $_GET['template'], 'stylesheet' => @$_GET['stylesheet'] ), $matches[3] );
1123        if ( 0 === strpos($link, 'preview=1') )
1124                $link = "?$link";
1125        return $matches[1] . esc_attr( $link ) . $matches[4];
1126}
1127
1128/**
1129 * Switches current theme to new template and stylesheet names.
1130 *
1131 * @since unknown
1132 * @uses do_action() Calls 'switch_theme' action on updated theme display name.
1133 *
1134 * @param string $template Template name
1135 * @param string $stylesheet Stylesheet name.
1136 */
1137function switch_theme($template, $stylesheet) {
1138        update_option('template', $template);
1139        update_option('stylesheet', $stylesheet);
1140        delete_option('current_theme');
1141        $theme = get_current_theme();
1142        do_action('switch_theme', $theme);
1143}
1144
1145/**
1146 * Checks that current theme files 'index.php' and 'style.css' exists.
1147 *
1148 * Does not check the 'default' theme. The 'default' theme should always exist
1149 * or should have another theme renamed to that template name and directory
1150 * path. Will switch theme to default if current theme does not validate.
1151 * You can use the 'validate_current_theme' filter to return FALSE to
1152 * disable this functionality.
1153 *
1154 * @since 1.5.0
1155 *
1156 * @return bool
1157 */
1158function validate_current_theme() {
1159        // Don't validate during an install/upgrade.
1160        if ( defined('WP_INSTALLING') || !apply_filters( 'validate_current_theme', true ) )
1161                return true;
1162
1163        if ( get_template() != 'default' && !file_exists(get_template_directory() . '/index.php') ) {
1164                switch_theme('default', 'default');
1165                return false;
1166        }
1167
1168        if ( get_stylesheet() != 'default' && !file_exists(get_template_directory() . '/style.css') ) {
1169                switch_theme('default', 'default');
1170                return false;
1171        }
1172
1173        return true;
1174}
1175
1176/**
1177 * Retrieve theme modification value for the current theme.
1178 *
1179 * If the modification name does not exist, then the $default will be passed
1180 * through {@link http://php.net/sprintf sprintf()} PHP function with the first
1181 * string the template directory URI and the second string the stylesheet
1182 * directory URI.
1183 *
1184 * @since 2.1.0
1185 * @uses apply_filters() Calls 'theme_mod_$name' filter on the value.
1186 *
1187 * @param string $name Theme modification name.
1188 * @param bool|string $default
1189 * @return string
1190 */
1191function get_theme_mod($name, $default = false) {
1192        $theme = get_current_theme();
1193
1194        $mods = get_option("mods_$theme");
1195
1196        if ( isset($mods[$name]) )
1197                return apply_filters( "theme_mod_$name", $mods[$name] );
1198
1199        return apply_filters( "theme_mod_$name", sprintf($default, get_template_directory_uri(), get_stylesheet_directory_uri()) );
1200}
1201
1202/**
1203 * Update theme modification value for the current theme.
1204 *
1205 * @since 2.1.0
1206 *
1207 * @param string $name Theme modification name.
1208 * @param string $value theme modification value.
1209 */
1210function set_theme_mod($name, $value) {
1211        $theme = get_current_theme();
1212
1213        $mods = get_option("mods_$theme");
1214
1215        $mods[$name] = $value;
1216
1217        update_option("mods_$theme", $mods);
1218        wp_cache_delete("mods_$theme", 'options');
1219}
1220
1221/**
1222 * Remove theme modification name from current theme list.
1223 *
1224 * If removing the name also removes all elements, then the entire option will
1225 * be removed.
1226 *
1227 * @since 2.1.0
1228 *
1229 * @param string $name Theme modification name.
1230 * @return null
1231 */
1232function remove_theme_mod( $name ) {
1233        $theme = get_current_theme();
1234
1235        $mods = get_option("mods_$theme");
1236
1237        if ( !isset($mods[$name]) )
1238                return;
1239
1240        unset($mods[$name]);
1241
1242        if ( empty($mods) )
1243                return remove_theme_mods();
1244
1245        update_option("mods_$theme", $mods);
1246        wp_cache_delete("mods_$theme", 'options');
1247}
1248
1249/**
1250 * Remove theme modifications option for current theme.
1251 *
1252 * @since 2.1.0
1253 */
1254function remove_theme_mods() {
1255        $theme = get_current_theme();
1256
1257        delete_option("mods_$theme");
1258}
1259
1260/**
1261 * Retrieve text color for custom header.
1262 *
1263 * @since 2.1.0
1264 * @uses HEADER_TEXTCOLOR
1265 *
1266 * @return string
1267 */
1268function get_header_textcolor() {
1269        return get_theme_mod('header_textcolor', HEADER_TEXTCOLOR);
1270}
1271
1272/**
1273 * Display text color for custom header.
1274 *
1275 * @since 2.1.0
1276 */
1277function header_textcolor() {
1278        echo get_header_textcolor();
1279}
1280
1281/**
1282 * Retrieve header image for custom header.
1283 *
1284 * @since 2.1.0
1285 * @uses HEADER_IMAGE
1286 *
1287 * @return string
1288 */
1289function get_header_image() {
1290        return get_theme_mod('header_image', HEADER_IMAGE);
1291}
1292
1293/**
1294 * Display header image path.
1295 *
1296 * @since 2.1.0
1297 */
1298function header_image() {
1299        echo get_header_image();
1300}
1301
1302/**
1303 * Add callbacks for image header display.
1304 *
1305 * The parameter $header_callback callback will be required to display the
1306 * content for the 'wp_head' action. The parameter $admin_header_callback
1307 * callback will be added to Custom_Image_Header class and that will be added
1308 * to the 'admin_menu' action.
1309 *
1310 * @since 2.1.0
1311 * @uses Custom_Image_Header Sets up for $admin_header_callback for administration panel display.
1312 *
1313 * @param callback $header_callback Call on 'wp_head' action.
1314 * @param callback $admin_header_callback Call on custom header administration screen.
1315 * @param callback $admin_image_div_callback Output a custom header image div on the custom header administration screen. Optional.
1316 */
1317function add_custom_image_header($header_callback, $admin_header_callback, $admin_image_div_callback = '') {
1318        if ( ! empty($header_callback) )
1319                add_action('wp_head', $header_callback);
1320
1321        if ( ! is_admin() )
1322                return;
1323        require_once(ABSPATH . 'wp-admin/custom-header.php');
1324        $GLOBALS['custom_image_header'] =& new Custom_Image_Header($admin_header_callback, $admin_image_div_callback);
1325        add_action('admin_menu', array(&$GLOBALS['custom_image_header'], 'init'));
1326}
1327
1328/**
1329 * Allows a theme to register its support of a certain feature
1330 *
1331 * Must be called in the themes functions.php file to work.
1332 *
1333 * @author Mark Jaquith
1334 * @since 2.9
1335 * @param string $feature the feature being added
1336 */
1337function add_theme_support( $feature ) {
1338        global $_wp_theme_features;
1339
1340        if ( func_num_args() == 1 )
1341                $_wp_theme_features[$feature] = true;
1342        else
1343                $_wp_theme_features[$feature] = array_slice( func_get_args(), 1 );
1344}
1345
1346/**
1347 * Checks a theme's support for a given feature
1348 *
1349 * @author Mark Jaquith
1350 * @since 2.9
1351 * @param string $feature the feature being checked
1352 * @return boolean
1353 */
1354
1355function current_theme_supports( $feature ) {
1356        global $_wp_theme_features;
1357
1358        if ( !isset( $_wp_theme_features[$feature] ) )
1359                return false;
1360
1361        // If no args passed then no extra checks need be performed
1362        if ( func_num_args() <= 1 )
1363                return true;
1364
1365        $args = array_slice( func_get_args(), 1 );
1366
1367        // @todo Allow pluggable arg checking
1368        switch ( $feature ) {
1369                case 'post-thumbnails':
1370                        // post-thumbnails can be registered for only certain content/post types by passing
1371                        // an array of types to add_theme_support().  If no array was passed, then
1372                        // any type is accepted
1373                        if ( true === $_wp_theme_features[$feature] )  // Registered for all types
1374                                return true;
1375                        $content_type = $args[0];
1376                        if ( in_array($content_type, $_wp_theme_features[$feature][0]) )
1377                                return true;
1378                        else
1379                                return false;
1380                        break;
1381        }
1382
1383        return true;
1384}
1385
1386/**
1387 * Checks a theme's support for a given feature before loading the functions which implement it.
1388 *
1389 * @author Peter Westwood
1390 * @since 2.9
1391 * @param string $feature the feature being checked
1392 * @param string $include the file containing the functions that implement the feature
1393 */
1394function require_if_theme_supports( $feature, $include) {
1395        if ( current_theme_supports( $feature ) )
1396                require ( $include );
1397}
1398
1399?>