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