~canonical-sysadmins/wordpress/4.7.2

« back to all changes in this revision

Viewing changes to wp-includes/theme.php

  • Committer: Jacek Nykis
  • Date: 2015-01-05 16:17:05 UTC
  • Revision ID: jacek.nykis@canonical.com-20150105161705-w544l1h5mcg7u4w9
Initial commit

Show diffs side-by-side

added added

removed removed

Lines of Context:
 
1
<?php
 
2
/**
 
3
 * Theme, template, and stylesheet functions.
 
4
 *
 
5
 * @package WordPress
 
6
 * @subpackage Theme
 
7
 */
 
8
 
 
9
/**
 
10
 * Returns an array of WP_Theme objects based on the arguments.
 
11
 *
 
12
 * Despite advances over get_themes(), this function is quite expensive, and grows
 
13
 * linearly with additional themes. Stick to wp_get_theme() if possible.
 
14
 *
 
15
 * @since 3.4.0
 
16
 *
 
17
 * @param array $args The search arguments. Optional.
 
18
 * - errors      mixed  True to return themes with errors, false to return themes without errors, null
 
19
 *                      to return all themes. Defaults to false.
 
20
 * - allowed     mixed  (Multisite) True to return only allowed themes for a site. False to return only
 
21
 *                      disallowed themes for a site. 'site' to return only site-allowed themes. 'network'
 
22
 *                      to return only network-allowed themes. Null to return all themes. Defaults to null.
 
23
 * - blog_id     int    (Multisite) The blog ID used to calculate which themes are allowed. Defaults to 0,
 
24
 *                      synonymous for the current blog.
 
25
 * @return Array of WP_Theme objects.
 
26
 */
 
27
function wp_get_themes( $args = array() ) {
 
28
        global $wp_theme_directories;
 
29
 
 
30
        $defaults = array( 'errors' => false, 'allowed' => null, 'blog_id' => 0 );
 
31
        $args = wp_parse_args( $args, $defaults );
 
32
 
 
33
        $theme_directories = search_theme_directories();
 
34
 
 
35
        if ( count( $wp_theme_directories ) > 1 ) {
 
36
                // Make sure the current theme wins out, in case search_theme_directories() picks the wrong
 
37
                // one in the case of a conflict. (Normally, last registered theme root wins.)
 
38
                $current_theme = get_stylesheet();
 
39
                if ( isset( $theme_directories[ $current_theme ] ) ) {
 
40
                        $root_of_current_theme = get_raw_theme_root( $current_theme );
 
41
                        if ( ! in_array( $root_of_current_theme, $wp_theme_directories ) )
 
42
                                $root_of_current_theme = WP_CONTENT_DIR . $root_of_current_theme;
 
43
                        $theme_directories[ $current_theme ]['theme_root'] = $root_of_current_theme;
 
44
                }
 
45
        }
 
46
 
 
47
        if ( empty( $theme_directories ) )
 
48
                return array();
 
49
 
 
50
        if ( is_multisite() && null !== $args['allowed'] ) {
 
51
                $allowed = $args['allowed'];
 
52
                if ( 'network' === $allowed )
 
53
                        $theme_directories = array_intersect_key( $theme_directories, WP_Theme::get_allowed_on_network() );
 
54
                elseif ( 'site' === $allowed )
 
55
                        $theme_directories = array_intersect_key( $theme_directories, WP_Theme::get_allowed_on_site( $args['blog_id'] ) );
 
56
                elseif ( $allowed )
 
57
                        $theme_directories = array_intersect_key( $theme_directories, WP_Theme::get_allowed( $args['blog_id'] ) );
 
58
                else
 
59
                        $theme_directories = array_diff_key( $theme_directories, WP_Theme::get_allowed( $args['blog_id'] ) );
 
60
        }
 
61
 
 
62
        $themes = array();
 
63
        static $_themes = array();
 
64
 
 
65
        foreach ( $theme_directories as $theme => $theme_root ) {
 
66
                if ( isset( $_themes[ $theme_root['theme_root'] . '/' . $theme ] ) )
 
67
                        $themes[ $theme ] = $_themes[ $theme_root['theme_root'] . '/' . $theme ];
 
68
                else
 
69
                        $themes[ $theme ] = $_themes[ $theme_root['theme_root'] . '/' . $theme ] = new WP_Theme( $theme, $theme_root['theme_root'] );
 
70
        }
 
71
 
 
72
        if ( null !== $args['errors'] ) {
 
73
                foreach ( $themes as $theme => $wp_theme ) {
 
74
                        if ( $wp_theme->errors() != $args['errors'] )
 
75
                                unset( $themes[ $theme ] );
 
76
                }
 
77
        }
 
78
 
 
79
        return $themes;
 
80
}
 
81
 
 
82
/**
 
83
 * Gets a WP_Theme object for a theme.
 
84
 *
 
85
 * @since 3.4.0
 
86
 *
 
87
 * @param string $stylesheet Directory name for the theme. Optional. Defaults to current theme.
 
88
 * @param string $theme_root Absolute path of the theme root to look in. Optional. If not specified, get_raw_theme_root()
 
89
 *      is used to calculate the theme root for the $stylesheet provided (or current theme).
 
90
 * @return WP_Theme Theme object. Be sure to check the object's exists() method if you need to confirm the theme's existence.
 
91
 */
 
92
function wp_get_theme( $stylesheet = null, $theme_root = null ) {
 
93
        global $wp_theme_directories;
 
94
 
 
95
        if ( empty( $stylesheet ) )
 
96
                $stylesheet = get_stylesheet();
 
97
 
 
98
        if ( empty( $theme_root ) ) {
 
99
                $theme_root = get_raw_theme_root( $stylesheet );
 
100
                if ( false === $theme_root )
 
101
                        $theme_root = WP_CONTENT_DIR . '/themes';
 
102
                elseif ( ! in_array( $theme_root, (array) $wp_theme_directories ) )
 
103
                        $theme_root = WP_CONTENT_DIR . $theme_root;
 
104
        }
 
105
 
 
106
        return new WP_Theme( $stylesheet, $theme_root );
 
107
}
 
108
 
 
109
/**
 
110
 * Clears the cache held by get_theme_roots() and WP_Theme.
 
111
 *
 
112
 * @since 3.5.0
 
113
 * @param bool $clear_update_cache Whether to clear the Theme updates cache
 
114
 */
 
115
function wp_clean_themes_cache( $clear_update_cache = true ) {
 
116
        if ( $clear_update_cache )
 
117
                delete_site_transient( 'update_themes' );
 
118
        search_theme_directories( true );
 
119
        foreach ( wp_get_themes( array( 'errors' => null ) ) as $theme )
 
120
                $theme->cache_delete();
 
121
}
 
122
 
 
123
/**
 
124
 * Whether a child theme is in use.
 
125
 *
 
126
 * @since 3.0.0
 
127
 *
 
128
 * @return bool true if a child theme is in use, false otherwise.
 
129
 **/
 
130
function is_child_theme() {
 
131
        return ( TEMPLATEPATH !== STYLESHEETPATH );
 
132
}
 
133
 
 
134
/**
 
135
 * Retrieve name of the current stylesheet.
 
136
 *
 
137
 * The theme name that the administrator has currently set the front end theme
 
138
 * as.
 
139
 *
 
140
 * For all intents and purposes, the template name and the stylesheet name are
 
141
 * going to be the same for most cases.
 
142
 *
 
143
 * @since 1.5.0
 
144
 *
 
145
 * @return string Stylesheet name.
 
146
 */
 
147
function get_stylesheet() {
 
148
        /**
 
149
         * Filter the name of current stylesheet.
 
150
         *
 
151
         * @since 1.5.0
 
152
         *
 
153
         * @param string $stylesheet Name of the current stylesheet.
 
154
         */
 
155
        return apply_filters( 'stylesheet', get_option( 'stylesheet' ) );
 
156
}
 
157
 
 
158
/**
 
159
 * Retrieve stylesheet directory path for current theme.
 
160
 *
 
161
 * @since 1.5.0
 
162
 *
 
163
 * @return string Path to current theme directory.
 
164
 */
 
165
function get_stylesheet_directory() {
 
166
        $stylesheet = get_stylesheet();
 
167
        $theme_root = get_theme_root( $stylesheet );
 
168
        $stylesheet_dir = "$theme_root/$stylesheet";
 
169
 
 
170
        /**
 
171
         * Filter the stylesheet directory path for current theme.
 
172
         *
 
173
         * @since 1.5.0
 
174
         *
 
175
         * @param string $stylesheet_dir Absolute path to the current them.
 
176
         * @param string $stylesheet     Directory name of the current theme.
 
177
         * @param string $theme_root     Absolute path to themes directory.
 
178
         */
 
179
        return apply_filters( 'stylesheet_directory', $stylesheet_dir, $stylesheet, $theme_root );
 
180
}
 
181
 
 
182
/**
 
183
 * Retrieve stylesheet directory URI.
 
184
 *
 
185
 * @since 1.5.0
 
186
 *
 
187
 * @return string
 
188
 */
 
189
function get_stylesheet_directory_uri() {
 
190
        $stylesheet = str_replace( '%2F', '/', rawurlencode( get_stylesheet() ) );
 
191
        $theme_root_uri = get_theme_root_uri( $stylesheet );
 
192
        $stylesheet_dir_uri = "$theme_root_uri/$stylesheet";
 
193
 
 
194
        /**
 
195
         * Filter the stylesheet directory URI.
 
196
         *
 
197
         * @since 1.5.0
 
198
         *
 
199
         * @param string $stylesheet_dir_uri Stylesheet directory URI.
 
200
         * @param string $stylesheet         Name of the activated theme's directory.
 
201
         * @param string $theme_root_uri     Themes root URI.
 
202
         */
 
203
        return apply_filters( 'stylesheet_directory_uri', $stylesheet_dir_uri, $stylesheet, $theme_root_uri );
 
204
}
 
205
 
 
206
/**
 
207
 * Retrieve URI of current theme stylesheet.
 
208
 *
 
209
 * The stylesheet file name is 'style.css' which is appended to {@link
 
210
 * get_stylesheet_directory_uri() stylesheet directory URI} path.
 
211
 *
 
212
 * @since 1.5.0
 
213
 *
 
214
 * @return string
 
215
 */
 
216
function get_stylesheet_uri() {
 
217
        $stylesheet_dir_uri = get_stylesheet_directory_uri();
 
218
        $stylesheet_uri = $stylesheet_dir_uri . '/style.css';
 
219
        /**
 
220
         * Filter the URI of the current theme stylesheet.
 
221
         *
 
222
         * @since 1.5.0
 
223
         *
 
224
         * @param string $stylesheet_uri     Stylesheet URI for the current theme/child theme.
 
225
         * @param string $stylesheet_dir_uri Stylesheet directory URI for the current theme/child theme.
 
226
         */
 
227
        return apply_filters( 'stylesheet_uri', $stylesheet_uri, $stylesheet_dir_uri );
 
228
}
 
229
 
 
230
/**
 
231
 * Retrieve localized stylesheet URI.
 
232
 *
 
233
 * The stylesheet directory for the localized stylesheet files are located, by
 
234
 * default, in the base theme directory. The name of the locale file will be the
 
235
 * locale followed by '.css'. If that does not exist, then the text direction
 
236
 * stylesheet will be checked for existence, for example 'ltr.css'.
 
237
 *
 
238
 * The theme may change the location of the stylesheet directory by either using
 
239
 * the 'stylesheet_directory_uri' filter or the 'locale_stylesheet_uri' filter.
 
240
 * If you want to change the location of the stylesheet files for the entire
 
241
 * WordPress workflow, then change the former. If you just have the locale in a
 
242
 * separate folder, then change the latter.
 
243
 *
 
244
 * @since 2.1.0
 
245
 *
 
246
 * @return string
 
247
 */
 
248
function get_locale_stylesheet_uri() {
 
249
        global $wp_locale;
 
250
        $stylesheet_dir_uri = get_stylesheet_directory_uri();
 
251
        $dir = get_stylesheet_directory();
 
252
        $locale = get_locale();
 
253
        if ( file_exists("$dir/$locale.css") )
 
254
                $stylesheet_uri = "$stylesheet_dir_uri/$locale.css";
 
255
        elseif ( !empty($wp_locale->text_direction) && file_exists("$dir/{$wp_locale->text_direction}.css") )
 
256
                $stylesheet_uri = "$stylesheet_dir_uri/{$wp_locale->text_direction}.css";
 
257
        else
 
258
                $stylesheet_uri = '';
 
259
        /**
 
260
         * Filter the localized stylesheet URI.
 
261
         *
 
262
         * @since 2.1.0
 
263
         *
 
264
         * @param string $stylesheet_uri     Localized stylesheet URI.
 
265
         * @param string $stylesheet_dir_uri Stylesheet directory URI.
 
266
         */
 
267
        return apply_filters( 'locale_stylesheet_uri', $stylesheet_uri, $stylesheet_dir_uri );
 
268
}
 
269
 
 
270
/**
 
271
 * Retrieve name of the current theme.
 
272
 *
 
273
 * @since 1.5.0
 
274
 * @uses apply_filters() Calls 'template' filter on template option.
 
275
 *
 
276
 * @return string Template name.
 
277
 */
 
278
function get_template() {
 
279
        /**
 
280
         * Filter the name of the current theme.
 
281
         *
 
282
         * @since 1.5.0
 
283
         *
 
284
         * @param string $template Current theme's directory name.
 
285
         */
 
286
        return apply_filters( 'template', get_option( 'template' ) );
 
287
}
 
288
 
 
289
/**
 
290
 * Retrieve current theme directory.
 
291
 *
 
292
 * @since 1.5.0
 
293
 *
 
294
 * @return string Template directory path.
 
295
 */
 
296
function get_template_directory() {
 
297
        $template = get_template();
 
298
        $theme_root = get_theme_root( $template );
 
299
        $template_dir = "$theme_root/$template";
 
300
 
 
301
        /**
 
302
         * Filter the current theme directory path.
 
303
         *
 
304
         * @since 1.5.0
 
305
         *
 
306
         * @param string $template_dir The URI of the current theme directory.
 
307
         * @param string $template     Directory name of the current theme.
 
308
         * @param string $theme_root   Absolute path to the themes directory.
 
309
         */
 
310
        return apply_filters( 'template_directory', $template_dir, $template, $theme_root );
 
311
}
 
312
 
 
313
/**
 
314
 * Retrieve theme directory URI.
 
315
 *
 
316
 * @since 1.5.0
 
317
 *
 
318
 * @return string Template directory URI.
 
319
 */
 
320
function get_template_directory_uri() {
 
321
        $template = str_replace( '%2F', '/', rawurlencode( get_template() ) );
 
322
        $theme_root_uri = get_theme_root_uri( $template );
 
323
        $template_dir_uri = "$theme_root_uri/$template";
 
324
 
 
325
        /**
 
326
         * Filter the current theme directory URI.
 
327
         *
 
328
         * @since 1.5.0
 
329
         *
 
330
         * @param string $template_dir_uri The URI of the current theme directory.
 
331
         * @param string $template         Directory name of the current theme.
 
332
         * @param string $theme_root_uri   The themes root URI.
 
333
         */
 
334
        return apply_filters( 'template_directory_uri', $template_dir_uri, $template, $theme_root_uri );
 
335
}
 
336
 
 
337
/**
 
338
 * Retrieve theme roots.
 
339
 *
 
340
 * @since 2.9.0
 
341
 *
 
342
 * @return array|string An array of theme roots keyed by template/stylesheet or a single theme root if all themes have the same root.
 
343
 */
 
344
function get_theme_roots() {
 
345
        global $wp_theme_directories;
 
346
 
 
347
        if ( count($wp_theme_directories) <= 1 )
 
348
                return '/themes';
 
349
 
 
350
        $theme_roots = get_site_transient( 'theme_roots' );
 
351
        if ( false === $theme_roots ) {
 
352
                search_theme_directories( true ); // Regenerate the transient.
 
353
                $theme_roots = get_site_transient( 'theme_roots' );
 
354
        }
 
355
        return $theme_roots;
 
356
}
 
357
 
 
358
/**
 
359
 * Register a directory that contains themes.
 
360
 *
 
361
 * @since 2.9.0
 
362
 *
 
363
 * @param string $directory Either the full filesystem path to a theme folder or a folder within WP_CONTENT_DIR
 
364
 * @return bool
 
365
 */
 
366
function register_theme_directory( $directory ) {
 
367
        global $wp_theme_directories;
 
368
 
 
369
        if ( ! file_exists( $directory ) ) {
 
370
                // Try prepending as the theme directory could be relative to the content directory
 
371
                $directory = WP_CONTENT_DIR . '/' . $directory;
 
372
                // If this directory does not exist, return and do not register
 
373
                if ( ! file_exists( $directory ) ) {
 
374
                        return false;
 
375
                }
 
376
        }
 
377
 
 
378
        if ( ! is_array( $wp_theme_directories ) ) {
 
379
                $wp_theme_directories = array();
 
380
        }
 
381
 
 
382
        $untrailed = untrailingslashit( $directory );
 
383
        if ( ! empty( $untrailed ) && ! in_array( $untrailed, $wp_theme_directories ) ) {
 
384
                $wp_theme_directories[] = $untrailed;
 
385
        }
 
386
 
 
387
        return true;
 
388
}
 
389
 
 
390
/**
 
391
 * Search all registered theme directories for complete and valid themes.
 
392
 *
 
393
 * @since 2.9.0
 
394
 *
 
395
 * @param bool $force Optional. Whether to force a new directory scan. Defaults to false.
 
396
 * @return array Valid themes found
 
397
 */
 
398
function search_theme_directories( $force = false ) {
 
399
        global $wp_theme_directories;
 
400
        if ( empty( $wp_theme_directories ) )
 
401
                return false;
 
402
 
 
403
        static $found_themes;
 
404
        if ( ! $force && isset( $found_themes ) )
 
405
                return $found_themes;
 
406
 
 
407
        $found_themes = array();
 
408
 
 
409
        $wp_theme_directories = (array) $wp_theme_directories;
 
410
 
 
411
        // Set up maybe-relative, maybe-absolute array of theme directories.
 
412
        // We always want to return absolute, but we need to cache relative
 
413
        // to use in get_theme_root().
 
414
        foreach ( $wp_theme_directories as $theme_root ) {
 
415
                if ( 0 === strpos( $theme_root, WP_CONTENT_DIR ) )
 
416
                        $relative_theme_roots[ str_replace( WP_CONTENT_DIR, '', $theme_root ) ] = $theme_root;
 
417
                else
 
418
                        $relative_theme_roots[ $theme_root ] = $theme_root;
 
419
        }
 
420
 
 
421
        /**
 
422
         * Filter whether to get the cache of the registered theme directories.
 
423
         *
 
424
         * @since 3.4.0
 
425
         *
 
426
         * @param bool   $cache_expiration Whether to get the cache of the theme directories. Default false.
 
427
         * @param string $cache_directory  Directory to be searched for the cache.
 
428
         */
 
429
        if ( $cache_expiration = apply_filters( 'wp_cache_themes_persistently', false, 'search_theme_directories' ) ) {
 
430
                $cached_roots = get_site_transient( 'theme_roots' );
 
431
                if ( is_array( $cached_roots ) ) {
 
432
                        foreach ( $cached_roots as $theme_dir => $theme_root ) {
 
433
                                // A cached theme root is no longer around, so skip it.
 
434
                                if ( ! isset( $relative_theme_roots[ $theme_root ] ) )
 
435
                                        continue;
 
436
                                $found_themes[ $theme_dir ] = array(
 
437
                                        'theme_file' => $theme_dir . '/style.css',
 
438
                                        'theme_root' => $relative_theme_roots[ $theme_root ], // Convert relative to absolute.
 
439
                                );
 
440
                        }
 
441
                        return $found_themes;
 
442
                }
 
443
                if ( ! is_int( $cache_expiration ) )
 
444
                        $cache_expiration = 1800; // half hour
 
445
        } else {
 
446
                $cache_expiration = 1800; // half hour
 
447
        }
 
448
 
 
449
        /* Loop the registered theme directories and extract all themes */
 
450
        foreach ( $wp_theme_directories as $theme_root ) {
 
451
 
 
452
                // Start with directories in the root of the current theme directory.
 
453
                $dirs = @ scandir( $theme_root );
 
454
                if ( ! $dirs ) {
 
455
                        trigger_error( "$theme_root is not readable", E_USER_NOTICE );
 
456
                        continue;
 
457
                }
 
458
                foreach ( $dirs as $dir ) {
 
459
                        if ( ! is_dir( $theme_root . '/' . $dir ) || $dir[0] == '.' || $dir == 'CVS' )
 
460
                                continue;
 
461
                        if ( file_exists( $theme_root . '/' . $dir . '/style.css' ) ) {
 
462
                                // wp-content/themes/a-single-theme
 
463
                                // wp-content/themes is $theme_root, a-single-theme is $dir
 
464
                                $found_themes[ $dir ] = array(
 
465
                                        'theme_file' => $dir . '/style.css',
 
466
                                        'theme_root' => $theme_root,
 
467
                                );
 
468
                        } else {
 
469
                                $found_theme = false;
 
470
                                // wp-content/themes/a-folder-of-themes/*
 
471
                                // wp-content/themes is $theme_root, a-folder-of-themes is $dir, then themes are $sub_dirs
 
472
                                $sub_dirs = @ scandir( $theme_root . '/' . $dir );
 
473
                                if ( ! $sub_dirs ) {
 
474
                                        trigger_error( "$theme_root/$dir is not readable", E_USER_NOTICE );
 
475
                                        continue;
 
476
                                }
 
477
                                foreach ( $sub_dirs as $sub_dir ) {
 
478
                                        if ( ! is_dir( $theme_root . '/' . $dir . '/' . $sub_dir ) || $dir[0] == '.' || $dir == 'CVS' )
 
479
                                                continue;
 
480
                                        if ( ! file_exists( $theme_root . '/' . $dir . '/' . $sub_dir . '/style.css' ) )
 
481
                                                continue;
 
482
                                        $found_themes[ $dir . '/' . $sub_dir ] = array(
 
483
                                                'theme_file' => $dir . '/' . $sub_dir . '/style.css',
 
484
                                                'theme_root' => $theme_root,
 
485
                                        );
 
486
                                        $found_theme = true;
 
487
                                }
 
488
                                // Never mind the above, it's just a theme missing a style.css.
 
489
                                // Return it; WP_Theme will catch the error.
 
490
                                if ( ! $found_theme )
 
491
                                        $found_themes[ $dir ] = array(
 
492
                                                'theme_file' => $dir . '/style.css',
 
493
                                                'theme_root' => $theme_root,
 
494
                                        );
 
495
                        }
 
496
                }
 
497
        }
 
498
 
 
499
        asort( $found_themes );
 
500
 
 
501
        $theme_roots = array();
 
502
        $relative_theme_roots = array_flip( $relative_theme_roots );
 
503
 
 
504
        foreach ( $found_themes as $theme_dir => $theme_data ) {
 
505
                $theme_roots[ $theme_dir ] = $relative_theme_roots[ $theme_data['theme_root'] ]; // Convert absolute to relative.
 
506
        }
 
507
 
 
508
        if ( $theme_roots != get_site_transient( 'theme_roots' ) )
 
509
                set_site_transient( 'theme_roots', $theme_roots, $cache_expiration );
 
510
 
 
511
        return $found_themes;
 
512
}
 
513
 
 
514
/**
 
515
 * Retrieve path to themes directory.
 
516
 *
 
517
 * Does not have trailing slash.
 
518
 *
 
519
 * @since 1.5.0
 
520
 *
 
521
 * @param string $stylesheet_or_template The stylesheet or template name of the theme
 
522
 * @return string Theme path.
 
523
 */
 
524
function get_theme_root( $stylesheet_or_template = false ) {
 
525
        global $wp_theme_directories;
 
526
 
 
527
        if ( $stylesheet_or_template && $theme_root = get_raw_theme_root( $stylesheet_or_template ) ) {
 
528
                // Always prepend WP_CONTENT_DIR unless the root currently registered as a theme directory.
 
529
                // This gives relative theme roots the benefit of the doubt when things go haywire.
 
530
                if ( ! in_array( $theme_root, (array) $wp_theme_directories ) )
 
531
                        $theme_root = WP_CONTENT_DIR . $theme_root;
 
532
        } else {
 
533
                $theme_root = WP_CONTENT_DIR . '/themes';
 
534
        }
 
535
 
 
536
        /**
 
537
         * Filter the absolute path to the themes directory.
 
538
         *
 
539
         * @since 1.5.0
 
540
         *
 
541
         * @param string $theme_root Absolute path to themes directory.
 
542
         */
 
543
        return apply_filters( 'theme_root', $theme_root );
 
544
}
 
545
 
 
546
/**
 
547
 * Retrieve URI for themes directory.
 
548
 *
 
549
 * Does not have trailing slash.
 
550
 *
 
551
 * @since 1.5.0
 
552
 *
 
553
 * @param string $stylesheet_or_template Optional. The stylesheet or template name of the theme.
 
554
 *      Default is to leverage the main theme root.
 
555
 * @param string $theme_root Optional. The theme root for which calculations will be based, preventing
 
556
 *      the need for a get_raw_theme_root() call.
 
557
 * @return string Themes URI.
 
558
 */
 
559
function get_theme_root_uri( $stylesheet_or_template = false, $theme_root = false ) {
 
560
        global $wp_theme_directories;
 
561
 
 
562
        if ( $stylesheet_or_template && ! $theme_root )
 
563
                $theme_root = get_raw_theme_root( $stylesheet_or_template );
 
564
 
 
565
        if ( $stylesheet_or_template && $theme_root ) {
 
566
                if ( in_array( $theme_root, (array) $wp_theme_directories ) ) {
 
567
                        // Absolute path. Make an educated guess. YMMV -- but note the filter below.
 
568
                        if ( 0 === strpos( $theme_root, WP_CONTENT_DIR ) )
 
569
                                $theme_root_uri = content_url( str_replace( WP_CONTENT_DIR, '', $theme_root ) );
 
570
                        elseif ( 0 === strpos( $theme_root, ABSPATH ) )
 
571
                                $theme_root_uri = site_url( str_replace( ABSPATH, '', $theme_root ) );
 
572
                        elseif ( 0 === strpos( $theme_root, WP_PLUGIN_DIR ) || 0 === strpos( $theme_root, WPMU_PLUGIN_DIR ) )
 
573
                                $theme_root_uri = plugins_url( basename( $theme_root ), $theme_root );
 
574
                        else
 
575
                                $theme_root_uri = $theme_root;
 
576
                } else {
 
577
                        $theme_root_uri = content_url( $theme_root );
 
578
                }
 
579
        } else {
 
580
                $theme_root_uri = content_url( 'themes' );
 
581
        }
 
582
 
 
583
        /**
 
584
         * Filter the URI for themes directory.
 
585
         *
 
586
         * @since 1.5.0
 
587
         *
 
588
         * @param string $theme_root_uri         The URI for themes directory.
 
589
         * @param string $siteurl                WordPress web address which is set in General Options.
 
590
         * @param string $stylesheet_or_template Stylesheet or template name of the theme.
 
591
         */
 
592
        return apply_filters( 'theme_root_uri', $theme_root_uri, get_option( 'siteurl' ), $stylesheet_or_template );
 
593
}
 
594
 
 
595
/**
 
596
 * Get the raw theme root relative to the content directory with no filters applied.
 
597
 *
 
598
 * @since 3.1.0
 
599
 *
 
600
 * @param string $stylesheet_or_template The stylesheet or template name of the theme
 
601
 * @param bool $skip_cache Optional. Whether to skip the cache. Defaults to false, meaning the cache is used.
 
602
 * @return string Theme root
 
603
 */
 
604
function get_raw_theme_root( $stylesheet_or_template, $skip_cache = false ) {
 
605
        global $wp_theme_directories;
 
606
 
 
607
        if ( count($wp_theme_directories) <= 1 )
 
608
                return '/themes';
 
609
 
 
610
        $theme_root = false;
 
611
 
 
612
        // If requesting the root for the current theme, consult options to avoid calling get_theme_roots()
 
613
        if ( ! $skip_cache ) {
 
614
                if ( get_option('stylesheet') == $stylesheet_or_template )
 
615
                        $theme_root = get_option('stylesheet_root');
 
616
                elseif ( get_option('template') == $stylesheet_or_template )
 
617
                        $theme_root = get_option('template_root');
 
618
        }
 
619
 
 
620
        if ( empty($theme_root) ) {
 
621
                $theme_roots = get_theme_roots();
 
622
                if ( !empty($theme_roots[$stylesheet_or_template]) )
 
623
                        $theme_root = $theme_roots[$stylesheet_or_template];
 
624
        }
 
625
 
 
626
        return $theme_root;
 
627
}
 
628
 
 
629
/**
 
630
 * Display localized stylesheet link element.
 
631
 *
 
632
 * @since 2.1.0
 
633
 */
 
634
function locale_stylesheet() {
 
635
        $stylesheet = get_locale_stylesheet_uri();
 
636
        if ( empty($stylesheet) )
 
637
                return;
 
638
        echo '<link rel="stylesheet" href="' . $stylesheet . '" type="text/css" media="screen" />';
 
639
}
 
640
 
 
641
/**
 
642
 * Start preview theme output buffer.
 
643
 *
 
644
 * Will only perform task if the user has permissions and template and preview
 
645
 * query variables exist.
 
646
 *
 
647
 * @since 2.6.0
 
648
 */
 
649
function preview_theme() {
 
650
        if ( ! (isset($_GET['template']) && isset($_GET['preview'])) )
 
651
                return;
 
652
 
 
653
        if ( !current_user_can( 'switch_themes' ) )
 
654
                return;
 
655
 
 
656
        // Admin Thickbox requests
 
657
        if ( isset( $_GET['preview_iframe'] ) )
 
658
                show_admin_bar( false );
 
659
 
 
660
        $_GET['template'] = preg_replace('|[^a-z0-9_./-]|i', '', $_GET['template']);
 
661
 
 
662
        if ( validate_file($_GET['template']) )
 
663
                return;
 
664
 
 
665
        add_filter( 'template', '_preview_theme_template_filter' );
 
666
 
 
667
        if ( isset($_GET['stylesheet']) ) {
 
668
                $_GET['stylesheet'] = preg_replace('|[^a-z0-9_./-]|i', '', $_GET['stylesheet']);
 
669
                if ( validate_file($_GET['stylesheet']) )
 
670
                        return;
 
671
                add_filter( 'stylesheet', '_preview_theme_stylesheet_filter' );
 
672
        }
 
673
 
 
674
        // Prevent theme mods to current theme being used on theme being previewed
 
675
        add_filter( 'pre_option_theme_mods_' . get_option( 'stylesheet' ), '__return_empty_array' );
 
676
 
 
677
        ob_start( 'preview_theme_ob_filter' );
 
678
}
 
679
add_action('setup_theme', 'preview_theme');
 
680
 
 
681
/**
 
682
 * Private function to modify the current template when previewing a theme
 
683
 *
 
684
 * @since 2.9.0
 
685
 * @access private
 
686
 *
 
687
 * @return string
 
688
 */
 
689
function _preview_theme_template_filter() {
 
690
        return isset($_GET['template']) ? $_GET['template'] : '';
 
691
}
 
692
 
 
693
/**
 
694
 * Private function to modify the current stylesheet when previewing a theme
 
695
 *
 
696
 * @since 2.9.0
 
697
 * @access private
 
698
 *
 
699
 * @return string
 
700
 */
 
701
function _preview_theme_stylesheet_filter() {
 
702
        return isset($_GET['stylesheet']) ? $_GET['stylesheet'] : '';
 
703
}
 
704
 
 
705
/**
 
706
 * Callback function for ob_start() to capture all links in the theme.
 
707
 *
 
708
 * @since 2.6.0
 
709
 * @access private
 
710
 *
 
711
 * @param string $content
 
712
 * @return string
 
713
 */
 
714
function preview_theme_ob_filter( $content ) {
 
715
        return preg_replace_callback( "|(<a.*?href=([\"']))(.*?)([\"'].*?>)|", 'preview_theme_ob_filter_callback', $content );
 
716
}
 
717
 
 
718
/**
 
719
 * Manipulates preview theme links in order to control and maintain location.
 
720
 *
 
721
 * Callback function for preg_replace_callback() to accept and filter matches.
 
722
 *
 
723
 * @since 2.6.0
 
724
 * @access private
 
725
 *
 
726
 * @param array $matches
 
727
 * @return string
 
728
 */
 
729
function preview_theme_ob_filter_callback( $matches ) {
 
730
        if ( strpos($matches[4], 'onclick') !== false )
 
731
                $matches[4] = preg_replace('#onclick=([\'"]).*?(?<!\\\)\\1#i', '', $matches[4]); //Strip out any onclicks from rest of <a>. (?<!\\\) means to ignore the '" if it's escaped by \  to prevent breaking mid-attribute.
 
732
        if (
 
733
                ( false !== strpos($matches[3], '/wp-admin/') )
 
734
        ||
 
735
                ( false !== strpos( $matches[3], '://' ) && 0 !== strpos( $matches[3], home_url() ) )
 
736
        ||
 
737
                ( false !== strpos($matches[3], '/feed/') )
 
738
        ||
 
739
                ( false !== strpos($matches[3], '/trackback/') )
 
740
        )
 
741
                return $matches[1] . "#$matches[2] onclick=$matches[2]return false;" . $matches[4];
 
742
 
 
743
        $stylesheet = isset( $_GET['stylesheet'] ) ? $_GET['stylesheet'] : '';
 
744
        $template   = isset( $_GET['template'] )   ? $_GET['template']   : '';
 
745
 
 
746
        $link = add_query_arg( array( 'preview' => 1, 'template' => $template, 'stylesheet' => $stylesheet, 'preview_iframe' => 1 ), $matches[3] );
 
747
        if ( 0 === strpos($link, 'preview=1') )
 
748
                $link = "?$link";
 
749
        return $matches[1] . esc_attr( $link ) . $matches[4];
 
750
}
 
751
 
 
752
/**
 
753
 * Switches the theme.
 
754
 *
 
755
 * Accepts one argument: $stylesheet of the theme. It also accepts an additional function signature
 
756
 * of two arguments: $template then $stylesheet. This is for backwards compatibility.
 
757
 *
 
758
 * @since 2.5.0
 
759
 *
 
760
 * @param string $stylesheet Stylesheet name
 
761
 */
 
762
function switch_theme( $stylesheet ) {
 
763
        global $wp_theme_directories, $wp_customize, $sidebars_widgets;
 
764
 
 
765
        $_sidebars_widgets = null;
 
766
        if ( 'wp_ajax_customize_save' === current_action() ) {
 
767
                $_sidebars_widgets = $wp_customize->post_value( $wp_customize->get_setting( 'old_sidebars_widgets_data' ) );
 
768
        } elseif ( is_array( $sidebars_widgets ) ) {
 
769
                $_sidebars_widgets = $sidebars_widgets;
 
770
        }
 
771
 
 
772
        if ( is_array( $_sidebars_widgets ) ) {
 
773
                set_theme_mod( 'sidebars_widgets', array( 'time' => time(), 'data' => $_sidebars_widgets ) );
 
774
        }
 
775
 
 
776
        $old_theme  = wp_get_theme();
 
777
        $new_theme = wp_get_theme( $stylesheet );
 
778
 
 
779
        if ( func_num_args() > 1 ) {
 
780
                $template = $stylesheet;
 
781
                $stylesheet = func_get_arg( 1 );
 
782
        } else {
 
783
                $template = $new_theme->get_template();
 
784
        }
 
785
 
 
786
        update_option( 'template', $template );
 
787
        update_option( 'stylesheet', $stylesheet );
 
788
 
 
789
        if ( count( $wp_theme_directories ) > 1 ) {
 
790
                update_option( 'template_root', get_raw_theme_root( $template, true ) );
 
791
                update_option( 'stylesheet_root', get_raw_theme_root( $stylesheet, true ) );
 
792
        } else {
 
793
                delete_option( 'template_root' );
 
794
                delete_option( 'stylesheet_root' );
 
795
        }
 
796
 
 
797
        $new_name  = $new_theme->get('Name');
 
798
 
 
799
        update_option( 'current_theme', $new_name );
 
800
 
 
801
        // Migrate from the old mods_{name} option to theme_mods_{slug}.
 
802
        if ( is_admin() && false === get_option( 'theme_mods_' . $stylesheet ) ) {
 
803
                $default_theme_mods = (array) get_option( 'mods_' . $new_name );
 
804
                add_option( "theme_mods_$stylesheet", $default_theme_mods );
 
805
        } else {
 
806
                /*
 
807
                 * Since retrieve_widgets() is called when initializing the customizer theme,
 
808
                 * we need to to remove the theme mods to avoid overwriting changes made via
 
809
                 * the widget customizer when accessing wp-admin/widgets.php.
 
810
                 */
 
811
                if ( 'wp_ajax_customize_save' === current_action() ) {
 
812
                        remove_theme_mod( 'sidebars_widgets' );
 
813
                }
 
814
        }
 
815
 
 
816
        update_option( 'theme_switched', $old_theme->get_stylesheet() );
 
817
        /**
 
818
         * Fires after the theme is switched.
 
819
         *
 
820
         * @since 1.5.0
 
821
         *
 
822
         * @param string   $new_name  Name of the new theme.
 
823
         * @param WP_Theme $new_theme WP_Theme instance of the new theme.
 
824
         */
 
825
        do_action( 'switch_theme', $new_name, $new_theme );
 
826
}
 
827
 
 
828
/**
 
829
 * Checks that current theme files 'index.php' and 'style.css' exists.
 
830
 *
 
831
 * Does not check the default theme, which is the fallback and should always exist.
 
832
 * Will switch theme to the fallback theme if current theme does not validate.
 
833
 * You can use the 'validate_current_theme' filter to return false to
 
834
 * disable this functionality.
 
835
 *
 
836
 * @since 1.5.0
 
837
 * @see WP_DEFAULT_THEME
 
838
 *
 
839
 * @return bool
 
840
 */
 
841
function validate_current_theme() {
 
842
        /**
 
843
         * Filter whether to validate the current theme.
 
844
         *
 
845
         * @since 2.7.0
 
846
         *
 
847
         * @param bool true Validation flag to check the current theme.
 
848
         */
 
849
        if ( defined('WP_INSTALLING') || ! apply_filters( 'validate_current_theme', true ) )
 
850
                return true;
 
851
 
 
852
        if ( get_template() != WP_DEFAULT_THEME && !file_exists(get_template_directory() . '/index.php') ) {
 
853
                switch_theme( WP_DEFAULT_THEME );
 
854
                return false;
 
855
        }
 
856
 
 
857
        if ( get_stylesheet() != WP_DEFAULT_THEME && !file_exists(get_template_directory() . '/style.css') ) {
 
858
                switch_theme( WP_DEFAULT_THEME );
 
859
                return false;
 
860
        }
 
861
 
 
862
        if ( is_child_theme() && ! file_exists( get_stylesheet_directory() . '/style.css' ) ) {
 
863
                switch_theme( WP_DEFAULT_THEME );
 
864
                return false;
 
865
        }
 
866
 
 
867
        return true;
 
868
}
 
869
 
 
870
/**
 
871
 * Retrieve all theme modifications.
 
872
 *
 
873
 * @since 3.1.0
 
874
 *
 
875
 * @return array Theme modifications.
 
876
 */
 
877
function get_theme_mods() {
 
878
        $theme_slug = get_option( 'stylesheet' );
 
879
        if ( false === ( $mods = get_option( "theme_mods_$theme_slug" ) ) ) {
 
880
                $theme_name = get_option( 'current_theme' );
 
881
                if ( false === $theme_name )
 
882
                        $theme_name = wp_get_theme()->get('Name');
 
883
                $mods = get_option( "mods_$theme_name" ); // Deprecated location.
 
884
                if ( is_admin() && false !== $mods ) {
 
885
                        update_option( "theme_mods_$theme_slug", $mods );
 
886
                        delete_option( "mods_$theme_name" );
 
887
                }
 
888
        }
 
889
        return $mods;
 
890
}
 
891
 
 
892
/**
 
893
 * Retrieve theme modification value for the current theme.
 
894
 *
 
895
 * If the modification name does not exist, then the $default will be passed
 
896
 * through {@link http://php.net/sprintf sprintf()} PHP function with the first
 
897
 * string the template directory URI and the second string the stylesheet
 
898
 * directory URI.
 
899
 *
 
900
 * @since 2.1.0
 
901
 *
 
902
 * @param string $name Theme modification name.
 
903
 * @param bool|string $default
 
904
 * @return string
 
905
 */
 
906
function get_theme_mod( $name, $default = false ) {
 
907
        $mods = get_theme_mods();
 
908
 
 
909
        if ( isset( $mods[$name] ) ) {
 
910
                /**
 
911
                 * Filter the theme modification, or 'theme_mod', value.
 
912
                 *
 
913
                 * The dynamic portion of the hook name, $name, refers to
 
914
                 * the key name of the modification array. For example,
 
915
                 * 'header_textcolor', 'header_image', and so on depending
 
916
                 * on the theme options.
 
917
                 *
 
918
                 * @since 2.2.0
 
919
                 *
 
920
                 * @param string $current_mod The value of the current theme modification.
 
921
                 */
 
922
                return apply_filters( "theme_mod_{$name}", $mods[$name] );
 
923
        }
 
924
 
 
925
        if ( is_string( $default ) )
 
926
                $default = sprintf( $default, get_template_directory_uri(), get_stylesheet_directory_uri() );
 
927
 
 
928
        /** This filter is documented in wp-includes/theme.php */
 
929
        return apply_filters( "theme_mod_{$name}", $default );
 
930
}
 
931
 
 
932
/**
 
933
 * Update theme modification value for the current theme.
 
934
 *
 
935
 * @since 2.1.0
 
936
 *
 
937
 * @param string $name Theme modification name.
 
938
 * @param string $value theme modification value.
 
939
 */
 
940
function set_theme_mod( $name, $value ) {
 
941
        $mods = get_theme_mods();
 
942
        $old_value = isset( $mods[ $name ] ) ? $mods[ $name ] : false;
 
943
 
 
944
        /**
 
945
         * Filter the theme mod value on save.
 
946
         *
 
947
         * The dynamic portion of the hook name, $name, refers to the key name of
 
948
         * the modification array. For example, 'header_textcolor', 'header_image',
 
949
         * and so on depending on the theme options.
 
950
         *
 
951
         * @since 3.9.0
 
952
         *
 
953
         * @param string $value     The new value of the theme mod.
 
954
         * @param string $old_value The current value of the theme mod.
 
955
         */
 
956
        $mods[ $name ] = apply_filters( "pre_set_theme_mod_$name", $value, $old_value );
 
957
 
 
958
        $theme = get_option( 'stylesheet' );
 
959
        update_option( "theme_mods_$theme", $mods );
 
960
}
 
961
 
 
962
/**
 
963
 * Remove theme modification name from current theme list.
 
964
 *
 
965
 * If removing the name also removes all elements, then the entire option will
 
966
 * be removed.
 
967
 *
 
968
 * @since 2.1.0
 
969
 *
 
970
 * @param string $name Theme modification name.
 
971
 * @return null
 
972
 */
 
973
function remove_theme_mod( $name ) {
 
974
        $mods = get_theme_mods();
 
975
 
 
976
        if ( ! isset( $mods[ $name ] ) )
 
977
                return;
 
978
 
 
979
        unset( $mods[ $name ] );
 
980
 
 
981
        if ( empty( $mods ) )
 
982
                return remove_theme_mods();
 
983
 
 
984
        $theme = get_option( 'stylesheet' );
 
985
        update_option( "theme_mods_$theme", $mods );
 
986
}
 
987
 
 
988
/**
 
989
 * Remove theme modifications option for current theme.
 
990
 *
 
991
 * @since 2.1.0
 
992
 */
 
993
function remove_theme_mods() {
 
994
        delete_option( 'theme_mods_' . get_option( 'stylesheet' ) );
 
995
 
 
996
        // Old style.
 
997
        $theme_name = get_option( 'current_theme' );
 
998
        if ( false === $theme_name )
 
999
                $theme_name = wp_get_theme()->get('Name');
 
1000
        delete_option( 'mods_' . $theme_name );
 
1001
}
 
1002
 
 
1003
/**
 
1004
 * Retrieve text color for custom header.
 
1005
 *
 
1006
 * @since 2.1.0
 
1007
 *
 
1008
 * @return string
 
1009
 */
 
1010
function get_header_textcolor() {
 
1011
        return get_theme_mod('header_textcolor', get_theme_support( 'custom-header', 'default-text-color' ) );
 
1012
}
 
1013
 
 
1014
/**
 
1015
 * Display text color for custom header.
 
1016
 *
 
1017
 * @since 2.1.0
 
1018
 */
 
1019
function header_textcolor() {
 
1020
        echo get_header_textcolor();
 
1021
}
 
1022
 
 
1023
/**
 
1024
 * Whether to display the header text.
 
1025
 *
 
1026
 * @since 3.4.0
 
1027
 *
 
1028
 * @return bool
 
1029
 */
 
1030
function display_header_text() {
 
1031
        if ( ! current_theme_supports( 'custom-header', 'header-text' ) )
 
1032
                return false;
 
1033
 
 
1034
        $text_color = get_theme_mod( 'header_textcolor', get_theme_support( 'custom-header', 'default-text-color' ) );
 
1035
        return 'blank' != $text_color;
 
1036
}
 
1037
 
 
1038
/**
 
1039
 * Retrieve header image for custom header.
 
1040
 *
 
1041
 * @since 2.1.0
 
1042
 *
 
1043
 * @return string
 
1044
 */
 
1045
function get_header_image() {
 
1046
        $url = get_theme_mod( 'header_image', get_theme_support( 'custom-header', 'default-image' ) );
 
1047
 
 
1048
        if ( 'remove-header' == $url )
 
1049
                return false;
 
1050
 
 
1051
        if ( is_random_header_image() )
 
1052
                $url = get_random_header_image();
 
1053
 
 
1054
        return esc_url_raw( set_url_scheme( $url ) );
 
1055
}
 
1056
 
 
1057
/**
 
1058
 * Get random header image data from registered images in theme.
 
1059
 *
 
1060
 * @since 3.4.0
 
1061
 *
 
1062
 * @access private
 
1063
 *
 
1064
 * @return string Path to header image
 
1065
 */
 
1066
 
 
1067
function _get_random_header_data() {
 
1068
        static $_wp_random_header;
 
1069
 
 
1070
        if ( empty( $_wp_random_header ) ) {
 
1071
                global $_wp_default_headers;
 
1072
                $header_image_mod = get_theme_mod( 'header_image', '' );
 
1073
                $headers = array();
 
1074
 
 
1075
                if ( 'random-uploaded-image' == $header_image_mod )
 
1076
                        $headers = get_uploaded_header_images();
 
1077
                elseif ( ! empty( $_wp_default_headers ) ) {
 
1078
                        if ( 'random-default-image' == $header_image_mod ) {
 
1079
                                $headers = $_wp_default_headers;
 
1080
                        } else {
 
1081
                                if ( current_theme_supports( 'custom-header', 'random-default' ) )
 
1082
                                        $headers = $_wp_default_headers;
 
1083
                        }
 
1084
                }
 
1085
 
 
1086
                if ( empty( $headers ) )
 
1087
                        return new stdClass;
 
1088
 
 
1089
                $_wp_random_header = (object) $headers[ array_rand( $headers ) ];
 
1090
 
 
1091
                $_wp_random_header->url =  sprintf( $_wp_random_header->url, get_template_directory_uri(), get_stylesheet_directory_uri() );
 
1092
                $_wp_random_header->thumbnail_url =  sprintf( $_wp_random_header->thumbnail_url, get_template_directory_uri(), get_stylesheet_directory_uri() );
 
1093
        }
 
1094
        return $_wp_random_header;
 
1095
}
 
1096
 
 
1097
/**
 
1098
 * Get random header image url from registered images in theme.
 
1099
 *
 
1100
 * @since 3.2.0
 
1101
 *
 
1102
 * @return string Path to header image
 
1103
 */
 
1104
 
 
1105
function get_random_header_image() {
 
1106
        $random_image = _get_random_header_data();
 
1107
        if ( empty( $random_image->url ) )
 
1108
                return '';
 
1109
        return $random_image->url;
 
1110
}
 
1111
 
 
1112
/**
 
1113
 * Check if random header image is in use.
 
1114
 *
 
1115
 * Always true if user expressly chooses the option in Appearance > Header.
 
1116
 * Also true if theme has multiple header images registered, no specific header image
 
1117
 * is chosen, and theme turns on random headers with add_theme_support().
 
1118
 *
 
1119
 * @since 3.2.0
 
1120
 *
 
1121
 * @param string $type The random pool to use. any|default|uploaded
 
1122
 * @return boolean
 
1123
 */
 
1124
function is_random_header_image( $type = 'any' ) {
 
1125
        $header_image_mod = get_theme_mod( 'header_image', get_theme_support( 'custom-header', 'default-image' ) );
 
1126
 
 
1127
        if ( 'any' == $type ) {
 
1128
                if ( 'random-default-image' == $header_image_mod || 'random-uploaded-image' == $header_image_mod || ( '' != get_random_header_image() && empty( $header_image_mod ) ) )
 
1129
                        return true;
 
1130
        } else {
 
1131
                if ( "random-$type-image" == $header_image_mod )
 
1132
                        return true;
 
1133
                elseif ( 'default' == $type && empty( $header_image_mod ) && '' != get_random_header_image() )
 
1134
                        return true;
 
1135
        }
 
1136
 
 
1137
        return false;
 
1138
}
 
1139
 
 
1140
/**
 
1141
 * Display header image URL.
 
1142
 *
 
1143
 * @since 2.1.0
 
1144
 */
 
1145
function header_image() {
 
1146
        echo esc_url( get_header_image() );
 
1147
}
 
1148
 
 
1149
/**
 
1150
 * Get the header images uploaded for the current theme.
 
1151
 *
 
1152
 * @since 3.2.0
 
1153
 *
 
1154
 * @return array
 
1155
 */
 
1156
function get_uploaded_header_images() {
 
1157
        $header_images = array();
 
1158
 
 
1159
        // @todo caching
 
1160
        $headers = get_posts( array( 'post_type' => 'attachment', 'meta_key' => '_wp_attachment_is_custom_header', 'meta_value' => get_option('stylesheet'), 'orderby' => 'none', 'nopaging' => true ) );
 
1161
 
 
1162
        if ( empty( $headers ) )
 
1163
                return array();
 
1164
 
 
1165
        foreach ( (array) $headers as $header ) {
 
1166
                $url = esc_url_raw( wp_get_attachment_url( $header->ID ) );
 
1167
                $header_data = wp_get_attachment_metadata( $header->ID );
 
1168
                $header_index = basename($url);
 
1169
                $header_images[$header_index] = array();
 
1170
                $header_images[$header_index]['attachment_id'] =  $header->ID;
 
1171
                $header_images[$header_index]['url'] =  $url;
 
1172
                $header_images[$header_index]['thumbnail_url'] =  $url;
 
1173
                if ( isset( $header_data['width'] ) )
 
1174
                        $header_images[$header_index]['width'] = $header_data['width'];
 
1175
                if ( isset( $header_data['height'] ) )
 
1176
                        $header_images[$header_index]['height'] = $header_data['height'];
 
1177
        }
 
1178
 
 
1179
        return $header_images;
 
1180
}
 
1181
 
 
1182
/**
 
1183
 * Get the header image data.
 
1184
 *
 
1185
 * @since 3.4.0
 
1186
 *
 
1187
 * @return object
 
1188
 */
 
1189
function get_custom_header() {
 
1190
        global $_wp_default_headers;
 
1191
 
 
1192
        if ( is_random_header_image() ) {
 
1193
                $data = _get_random_header_data();
 
1194
        } else {
 
1195
                $data = get_theme_mod( 'header_image_data' );
 
1196
                if ( ! $data && current_theme_supports( 'custom-header', 'default-image' ) ) {
 
1197
                        $directory_args = array( get_template_directory_uri(), get_stylesheet_directory_uri() );
 
1198
                        $data = array();
 
1199
                        $data['url'] = $data['thumbnail_url'] = vsprintf( get_theme_support( 'custom-header', 'default-image' ), $directory_args );
 
1200
                        if ( ! empty( $_wp_default_headers ) ) {
 
1201
                                foreach ( (array) $_wp_default_headers as $default_header ) {
 
1202
                                        $url = vsprintf( $default_header['url'], $directory_args );
 
1203
                                        if ( $data['url'] == $url ) {
 
1204
                                                $data = $default_header;
 
1205
                                                $data['url'] = $url;
 
1206
                                                $data['thumbnail_url'] = vsprintf( $data['thumbnail_url'], $directory_args );
 
1207
                                                break;
 
1208
                                        }
 
1209
                                }
 
1210
                        }
 
1211
                }
 
1212
        }
 
1213
 
 
1214
        $default = array(
 
1215
                'url'           => '',
 
1216
                'thumbnail_url' => '',
 
1217
                'width'         => get_theme_support( 'custom-header', 'width' ),
 
1218
                'height'        => get_theme_support( 'custom-header', 'height' ),
 
1219
        );
 
1220
        return (object) wp_parse_args( $data, $default );
 
1221
}
 
1222
 
 
1223
/**
 
1224
 * Register a selection of default headers to be displayed by the custom header admin UI.
 
1225
 *
 
1226
 * @since 3.0.0
 
1227
 *
 
1228
 * @param array $headers Array of headers keyed by a string id. The ids point to arrays containing 'url', 'thumbnail_url', and 'description' keys.
 
1229
 */
 
1230
function register_default_headers( $headers ) {
 
1231
        global $_wp_default_headers;
 
1232
 
 
1233
        $_wp_default_headers = array_merge( (array) $_wp_default_headers, (array) $headers );
 
1234
}
 
1235
 
 
1236
/**
 
1237
 * Unregister default headers.
 
1238
 *
 
1239
 * This function must be called after register_default_headers() has already added the
 
1240
 * header you want to remove.
 
1241
 *
 
1242
 * @see register_default_headers()
 
1243
 * @since 3.0.0
 
1244
 *
 
1245
 * @param string|array $header The header string id (key of array) to remove, or an array thereof.
 
1246
 * @return bool|void A single header returns true on success, false on failure.
 
1247
 *                   There is currently no return value for multiple headers.
 
1248
 */
 
1249
function unregister_default_headers( $header ) {
 
1250
        global $_wp_default_headers;
 
1251
        if ( is_array( $header ) ) {
 
1252
                array_map( 'unregister_default_headers', $header );
 
1253
        } elseif ( isset( $_wp_default_headers[ $header ] ) ) {
 
1254
                unset( $_wp_default_headers[ $header ] );
 
1255
                return true;
 
1256
        } else {
 
1257
                return false;
 
1258
        }
 
1259
}
 
1260
 
 
1261
/**
 
1262
 * Retrieve background image for custom background.
 
1263
 *
 
1264
 * @since 3.0.0
 
1265
 *
 
1266
 * @return string
 
1267
 */
 
1268
function get_background_image() {
 
1269
        return get_theme_mod('background_image', get_theme_support( 'custom-background', 'default-image' ) );
 
1270
}
 
1271
 
 
1272
/**
 
1273
 * Display background image path.
 
1274
 *
 
1275
 * @since 3.0.0
 
1276
 */
 
1277
function background_image() {
 
1278
        echo get_background_image();
 
1279
}
 
1280
 
 
1281
/**
 
1282
 * Retrieve value for custom background color.
 
1283
 *
 
1284
 * @since 3.0.0
 
1285
 *
 
1286
 * @return string
 
1287
 */
 
1288
function get_background_color() {
 
1289
        return get_theme_mod('background_color', get_theme_support( 'custom-background', 'default-color' ) );
 
1290
}
 
1291
 
 
1292
/**
 
1293
 * Display background color value.
 
1294
 *
 
1295
 * @since 3.0.0
 
1296
 */
 
1297
function background_color() {
 
1298
        echo get_background_color();
 
1299
}
 
1300
 
 
1301
/**
 
1302
 * Default custom background callback.
 
1303
 *
 
1304
 * @since 3.0.0
 
1305
 * @access protected
 
1306
 */
 
1307
function _custom_background_cb() {
 
1308
        // $background is the saved custom image, or the default image.
 
1309
        $background = set_url_scheme( get_background_image() );
 
1310
 
 
1311
        // $color is the saved custom color.
 
1312
        // A default has to be specified in style.css. It will not be printed here.
 
1313
        $color = get_background_color();
 
1314
 
 
1315
        if ( $color === get_theme_support( 'custom-background', 'default-color' ) ) {
 
1316
                $color = false;
 
1317
        }
 
1318
 
 
1319
        if ( ! $background && ! $color )
 
1320
                return;
 
1321
 
 
1322
        $style = $color ? "background-color: #$color;" : '';
 
1323
 
 
1324
        if ( $background ) {
 
1325
                $image = " background-image: url('$background');";
 
1326
 
 
1327
                $repeat = get_theme_mod( 'background_repeat', get_theme_support( 'custom-background', 'default-repeat' ) );
 
1328
                if ( ! in_array( $repeat, array( 'no-repeat', 'repeat-x', 'repeat-y', 'repeat' ) ) )
 
1329
                        $repeat = 'repeat';
 
1330
                $repeat = " background-repeat: $repeat;";
 
1331
 
 
1332
                $position = get_theme_mod( 'background_position_x', get_theme_support( 'custom-background', 'default-position-x' ) );
 
1333
                if ( ! in_array( $position, array( 'center', 'right', 'left' ) ) )
 
1334
                        $position = 'left';
 
1335
                $position = " background-position: top $position;";
 
1336
 
 
1337
                $attachment = get_theme_mod( 'background_attachment', get_theme_support( 'custom-background', 'default-attachment' ) );
 
1338
                if ( ! in_array( $attachment, array( 'fixed', 'scroll' ) ) )
 
1339
                        $attachment = 'scroll';
 
1340
                $attachment = " background-attachment: $attachment;";
 
1341
 
 
1342
                $style .= $image . $repeat . $position . $attachment;
 
1343
        }
 
1344
?>
 
1345
<style type="text/css" id="custom-background-css">
 
1346
body.custom-background { <?php echo trim( $style ); ?> }
 
1347
</style>
 
1348
<?php
 
1349
}
 
1350
 
 
1351
/**
 
1352
 * Add callback for custom TinyMCE editor stylesheets.
 
1353
 *
 
1354
 * The parameter $stylesheet is the name of the stylesheet, relative to
 
1355
 * the theme root. It also accepts an array of stylesheets.
 
1356
 * It is optional and defaults to 'editor-style.css'.
 
1357
 *
 
1358
 * This function automatically adds another stylesheet with -rtl prefix, e.g. editor-style-rtl.css.
 
1359
 * If that file doesn't exist, it is removed before adding the stylesheet(s) to TinyMCE.
 
1360
 * If an array of stylesheets is passed to add_editor_style(),
 
1361
 * RTL is only added for the first stylesheet.
 
1362
 *
 
1363
 * Since version 3.4 the TinyMCE body has .rtl CSS class.
 
1364
 * It is a better option to use that class and add any RTL styles to the main stylesheet.
 
1365
 *
 
1366
 * @since 3.0.0
 
1367
 *
 
1368
 * @param mixed $stylesheet Optional. Stylesheet name or array thereof, relative to theme root.
 
1369
 *      Defaults to 'editor-style.css'
 
1370
 */
 
1371
function add_editor_style( $stylesheet = 'editor-style.css' ) {
 
1372
 
 
1373
        add_theme_support( 'editor-style' );
 
1374
 
 
1375
        if ( ! is_admin() )
 
1376
                return;
 
1377
 
 
1378
        global $editor_styles;
 
1379
        $editor_styles = (array) $editor_styles;
 
1380
        $stylesheet    = (array) $stylesheet;
 
1381
        if ( is_rtl() ) {
 
1382
                $rtl_stylesheet = str_replace('.css', '-rtl.css', $stylesheet[0]);
 
1383
                $stylesheet[] = $rtl_stylesheet;
 
1384
        }
 
1385
 
 
1386
        $editor_styles = array_merge( $editor_styles, $stylesheet );
 
1387
}
 
1388
 
 
1389
/**
 
1390
 * Removes all visual editor stylesheets.
 
1391
 *
 
1392
 * @since 3.1.0
 
1393
 *
 
1394
 * @return bool True on success, false if there were no stylesheets to remove.
 
1395
 */
 
1396
function remove_editor_styles() {
 
1397
        if ( ! current_theme_supports( 'editor-style' ) )
 
1398
                return false;
 
1399
        _remove_theme_support( 'editor-style' );
 
1400
        if ( is_admin() )
 
1401
                $GLOBALS['editor_styles'] = array();
 
1402
        return true;
 
1403
}
 
1404
 
 
1405
/**
 
1406
 * Retrieve any registered editor stylesheets
 
1407
 *
 
1408
 * @since 4.0.0
 
1409
 *
 
1410
 * @global $editor_styles Registered editor stylesheets
 
1411
 *
 
1412
 * @return array If registered, a list of editor stylesheet URLs.
 
1413
 */
 
1414
function get_editor_stylesheets() {
 
1415
        $stylesheets = array();
 
1416
        // load editor_style.css if the current theme supports it
 
1417
        if ( ! empty( $GLOBALS['editor_styles'] ) && is_array( $GLOBALS['editor_styles'] ) ) {
 
1418
                $editor_styles = $GLOBALS['editor_styles'];
 
1419
 
 
1420
                $editor_styles = array_unique( array_filter( $editor_styles ) );
 
1421
                $style_uri = get_stylesheet_directory_uri();
 
1422
                $style_dir = get_stylesheet_directory();
 
1423
 
 
1424
                // Support externally referenced styles (like, say, fonts).
 
1425
                foreach ( $editor_styles as $key => $file ) {
 
1426
                        if ( preg_match( '~^(https?:)?//~', $file ) ) {
 
1427
                                $stylesheets[] = esc_url_raw( $file );
 
1428
                                unset( $editor_styles[ $key ] );
 
1429
                        }
 
1430
                }
 
1431
 
 
1432
                // Look in a parent theme first, that way child theme CSS overrides.
 
1433
                if ( is_child_theme() ) {
 
1434
                        $template_uri = get_template_directory_uri();
 
1435
                        $template_dir = get_template_directory();
 
1436
 
 
1437
                        foreach ( $editor_styles as $key => $file ) {
 
1438
                                if ( $file && file_exists( "$template_dir/$file" ) ) {
 
1439
                                        $stylesheets[] = "$template_uri/$file";
 
1440
                                }
 
1441
                        }
 
1442
                }
 
1443
 
 
1444
                foreach ( $editor_styles as $file ) {
 
1445
                        if ( $file && file_exists( "$style_dir/$file" ) ) {
 
1446
                                $stylesheets[] = "$style_uri/$file";
 
1447
                        }
 
1448
                }
 
1449
        }
 
1450
        return $stylesheets;
 
1451
}
 
1452
 
 
1453
/**
 
1454
 * Allows a theme to register its support of a certain feature
 
1455
 *
 
1456
 * Must be called in the theme's functions.php file to work.
 
1457
 * If attached to a hook, it must be after_setup_theme.
 
1458
 * The init hook may be too late for some features.
 
1459
 *
 
1460
 * @since 2.9.0
 
1461
 *
 
1462
 * @param string $feature The feature being added.
 
1463
 * @return void|bool False on failure, void otherwise.
 
1464
 */
 
1465
function add_theme_support( $feature ) {
 
1466
        global $_wp_theme_features;
 
1467
 
 
1468
        if ( func_num_args() == 1 )
 
1469
                $args = true;
 
1470
        else
 
1471
                $args = array_slice( func_get_args(), 1 );
 
1472
 
 
1473
        switch ( $feature ) {
 
1474
                case 'post-formats' :
 
1475
                        if ( is_array( $args[0] ) )
 
1476
                                $args[0] = array_intersect( $args[0], array_keys( get_post_format_slugs() ) );
 
1477
                        break;
 
1478
 
 
1479
                case 'html5' :
 
1480
                        // You can't just pass 'html5', you need to pass an array of types.
 
1481
                        if ( empty( $args[0] ) ) {
 
1482
                                // Build an array of types for back-compat.
 
1483
                                $args = array( 0 => array( 'comment-list', 'comment-form', 'search-form' ) );
 
1484
                        } elseif ( ! is_array( $args[0] ) ) {
 
1485
                                _doing_it_wrong( "add_theme_support( 'html5' )", 'You need to pass an array of types.', '3.6.1' );
 
1486
                                return false;
 
1487
                        }
 
1488
 
 
1489
                        // Calling 'html5' again merges, rather than overwrites.
 
1490
                        if ( isset( $_wp_theme_features['html5'] ) )
 
1491
                                $args[0] = array_merge( $_wp_theme_features['html5'][0], $args[0] );
 
1492
                        break;
 
1493
 
 
1494
                case 'custom-header-uploads' :
 
1495
                        return add_theme_support( 'custom-header', array( 'uploads' => true ) );
 
1496
 
 
1497
                case 'custom-header' :
 
1498
                        if ( ! is_array( $args ) )
 
1499
                                $args = array( 0 => array() );
 
1500
 
 
1501
                        $defaults = array(
 
1502
                                'default-image' => '',
 
1503
                                'random-default' => false,
 
1504
                                'width' => 0,
 
1505
                                'height' => 0,
 
1506
                                'flex-height' => false,
 
1507
                                'flex-width' => false,
 
1508
                                'default-text-color' => '',
 
1509
                                'header-text' => true,
 
1510
                                'uploads' => true,
 
1511
                                'wp-head-callback' => '',
 
1512
                                'admin-head-callback' => '',
 
1513
                                'admin-preview-callback' => '',
 
1514
                        );
 
1515
 
 
1516
                        $jit = isset( $args[0]['__jit'] );
 
1517
                        unset( $args[0]['__jit'] );
 
1518
 
 
1519
                        // Merge in data from previous add_theme_support() calls.
 
1520
                        // The first value registered wins. (A child theme is set up first.)
 
1521
                        if ( isset( $_wp_theme_features['custom-header'] ) )
 
1522
                                $args[0] = wp_parse_args( $_wp_theme_features['custom-header'][0], $args[0] );
 
1523
 
 
1524
                        // Load in the defaults at the end, as we need to insure first one wins.
 
1525
                        // This will cause all constants to be defined, as each arg will then be set to the default.
 
1526
                        if ( $jit )
 
1527
                                $args[0] = wp_parse_args( $args[0], $defaults );
 
1528
 
 
1529
                        // If a constant was defined, use that value. Otherwise, define the constant to ensure
 
1530
                        // the constant is always accurate (and is not defined later,  overriding our value).
 
1531
                        // As stated above, the first value wins.
 
1532
                        // Once we get to wp_loaded (just-in-time), define any constants we haven't already.
 
1533
                        // Constants are lame. Don't reference them. This is just for backwards compatibility.
 
1534
 
 
1535
                        if ( defined( 'NO_HEADER_TEXT' ) )
 
1536
                                $args[0]['header-text'] = ! NO_HEADER_TEXT;
 
1537
                        elseif ( isset( $args[0]['header-text'] ) )
 
1538
                                define( 'NO_HEADER_TEXT', empty( $args[0]['header-text'] ) );
 
1539
 
 
1540
                        if ( defined( 'HEADER_IMAGE_WIDTH' ) )
 
1541
                                $args[0]['width'] = (int) HEADER_IMAGE_WIDTH;
 
1542
                        elseif ( isset( $args[0]['width'] ) )
 
1543
                                define( 'HEADER_IMAGE_WIDTH', (int) $args[0]['width'] );
 
1544
 
 
1545
                        if ( defined( 'HEADER_IMAGE_HEIGHT' ) )
 
1546
                                $args[0]['height'] = (int) HEADER_IMAGE_HEIGHT;
 
1547
                        elseif ( isset( $args[0]['height'] ) )
 
1548
                                define( 'HEADER_IMAGE_HEIGHT', (int) $args[0]['height'] );
 
1549
 
 
1550
                        if ( defined( 'HEADER_TEXTCOLOR' ) )
 
1551
                                $args[0]['default-text-color'] = HEADER_TEXTCOLOR;
 
1552
                        elseif ( isset( $args[0]['default-text-color'] ) )
 
1553
                                define( 'HEADER_TEXTCOLOR', $args[0]['default-text-color'] );
 
1554
 
 
1555
                        if ( defined( 'HEADER_IMAGE' ) )
 
1556
                                $args[0]['default-image'] = HEADER_IMAGE;
 
1557
                        elseif ( isset( $args[0]['default-image'] ) )
 
1558
                                define( 'HEADER_IMAGE', $args[0]['default-image'] );
 
1559
 
 
1560
                        if ( $jit && ! empty( $args[0]['default-image'] ) )
 
1561
                                $args[0]['random-default'] = false;
 
1562
 
 
1563
                        // If headers are supported, and we still don't have a defined width or height,
 
1564
                        // we have implicit flex sizes.
 
1565
                        if ( $jit ) {
 
1566
                                if ( empty( $args[0]['width'] ) && empty( $args[0]['flex-width'] ) )
 
1567
                                        $args[0]['flex-width'] = true;
 
1568
                                if ( empty( $args[0]['height'] ) && empty( $args[0]['flex-height'] ) )
 
1569
                                        $args[0]['flex-height'] = true;
 
1570
                        }
 
1571
 
 
1572
                        break;
 
1573
 
 
1574
                case 'custom-background' :
 
1575
                        if ( ! is_array( $args ) )
 
1576
                                $args = array( 0 => array() );
 
1577
 
 
1578
                        $defaults = array(
 
1579
                                'default-image'          => '',
 
1580
                                'default-repeat'         => 'repeat',
 
1581
                                'default-position-x'     => 'left',
 
1582
                                'default-attachment'     => 'scroll',
 
1583
                                'default-color'          => '',
 
1584
                                'wp-head-callback'       => '_custom_background_cb',
 
1585
                                'admin-head-callback'    => '',
 
1586
                                'admin-preview-callback' => '',
 
1587
                        );
 
1588
 
 
1589
                        $jit = isset( $args[0]['__jit'] );
 
1590
                        unset( $args[0]['__jit'] );
 
1591
 
 
1592
                        // Merge in data from previous add_theme_support() calls. The first value registered wins.
 
1593
                        if ( isset( $_wp_theme_features['custom-background'] ) )
 
1594
                                $args[0] = wp_parse_args( $_wp_theme_features['custom-background'][0], $args[0] );
 
1595
 
 
1596
                        if ( $jit )
 
1597
                                $args[0] = wp_parse_args( $args[0], $defaults );
 
1598
 
 
1599
                        if ( defined( 'BACKGROUND_COLOR' ) )
 
1600
                                $args[0]['default-color'] = BACKGROUND_COLOR;
 
1601
                        elseif ( isset( $args[0]['default-color'] ) || $jit )
 
1602
                                define( 'BACKGROUND_COLOR', $args[0]['default-color'] );
 
1603
 
 
1604
                        if ( defined( 'BACKGROUND_IMAGE' ) )
 
1605
                                $args[0]['default-image'] = BACKGROUND_IMAGE;
 
1606
                        elseif ( isset( $args[0]['default-image'] ) || $jit )
 
1607
                                define( 'BACKGROUND_IMAGE', $args[0]['default-image'] );
 
1608
 
 
1609
                        break;
 
1610
        }
 
1611
 
 
1612
        $_wp_theme_features[ $feature ] = $args;
 
1613
}
 
1614
 
 
1615
/**
 
1616
 * Registers the internal custom header and background routines.
 
1617
 *
 
1618
 * @since 3.4.0
 
1619
 * @access private
 
1620
 */
 
1621
function _custom_header_background_just_in_time() {
 
1622
        global $custom_image_header, $custom_background;
 
1623
 
 
1624
        if ( current_theme_supports( 'custom-header' ) ) {
 
1625
                // In case any constants were defined after an add_custom_image_header() call, re-run.
 
1626
                add_theme_support( 'custom-header', array( '__jit' => true ) );
 
1627
 
 
1628
                $args = get_theme_support( 'custom-header' );
 
1629
                if ( $args[0]['wp-head-callback'] )
 
1630
                        add_action( 'wp_head', $args[0]['wp-head-callback'] );
 
1631
 
 
1632
                if ( is_admin() ) {
 
1633
                        require_once( ABSPATH . 'wp-admin/custom-header.php' );
 
1634
                        $custom_image_header = new Custom_Image_Header( $args[0]['admin-head-callback'], $args[0]['admin-preview-callback'] );
 
1635
                }
 
1636
        }
 
1637
 
 
1638
        if ( current_theme_supports( 'custom-background' ) ) {
 
1639
                // In case any constants were defined after an add_custom_background() call, re-run.
 
1640
                add_theme_support( 'custom-background', array( '__jit' => true ) );
 
1641
 
 
1642
                $args = get_theme_support( 'custom-background' );
 
1643
                add_action( 'wp_head', $args[0]['wp-head-callback'] );
 
1644
 
 
1645
                if ( is_admin() ) {
 
1646
                        require_once( ABSPATH . 'wp-admin/custom-background.php' );
 
1647
                        $custom_background = new Custom_Background( $args[0]['admin-head-callback'], $args[0]['admin-preview-callback'] );
 
1648
                }
 
1649
        }
 
1650
}
 
1651
add_action( 'wp_loaded', '_custom_header_background_just_in_time' );
 
1652
 
 
1653
/**
 
1654
 * Gets the theme support arguments passed when registering that support
 
1655
 *
 
1656
 * @since 3.1.0
 
1657
 *
 
1658
 * @param string $feature the feature to check
 
1659
 * @return array The array of extra arguments
 
1660
 */
 
1661
function get_theme_support( $feature ) {
 
1662
        global $_wp_theme_features;
 
1663
        if ( ! isset( $_wp_theme_features[ $feature ] ) )
 
1664
                return false;
 
1665
 
 
1666
        if ( func_num_args() <= 1 )
 
1667
                return $_wp_theme_features[ $feature ];
 
1668
 
 
1669
        $args = array_slice( func_get_args(), 1 );
 
1670
        switch ( $feature ) {
 
1671
                case 'custom-header' :
 
1672
                case 'custom-background' :
 
1673
                        if ( isset( $_wp_theme_features[ $feature ][0][ $args[0] ] ) )
 
1674
                                return $_wp_theme_features[ $feature ][0][ $args[0] ];
 
1675
                        return false;
 
1676
 
 
1677
                default :
 
1678
                        return $_wp_theme_features[ $feature ];
 
1679
        }
 
1680
}
 
1681
 
 
1682
/**
 
1683
 * Allows a theme to de-register its support of a certain feature
 
1684
 *
 
1685
 * Should be called in the theme's functions.php file. Generally would
 
1686
 * be used for child themes to override support from the parent theme.
 
1687
 *
 
1688
 * @since 3.0.0
 
1689
 * @see add_theme_support()
 
1690
 * @param string $feature the feature being added
 
1691
 * @return bool Whether feature was removed.
 
1692
 */
 
1693
function remove_theme_support( $feature ) {
 
1694
        // Blacklist: for internal registrations not used directly by themes.
 
1695
        if ( in_array( $feature, array( 'editor-style', 'widgets', 'menus' ) ) )
 
1696
                return false;
 
1697
 
 
1698
        return _remove_theme_support( $feature );
 
1699
}
 
1700
 
 
1701
/**
 
1702
 * Do not use. Removes theme support internally, ignorant of the blacklist.
 
1703
 *
 
1704
 * @access private
 
1705
 * @since 3.1.0
 
1706
 */
 
1707
function _remove_theme_support( $feature ) {
 
1708
        global $_wp_theme_features;
 
1709
 
 
1710
        switch ( $feature ) {
 
1711
                case 'custom-header-uploads' :
 
1712
                        if ( ! isset( $_wp_theme_features['custom-header'] ) )
 
1713
                                return false;
 
1714
                        add_theme_support( 'custom-header', array( 'uploads' => false ) );
 
1715
                        return; // Do not continue - custom-header-uploads no longer exists.
 
1716
        }
 
1717
 
 
1718
        if ( ! isset( $_wp_theme_features[ $feature ] ) )
 
1719
                return false;
 
1720
 
 
1721
        switch ( $feature ) {
 
1722
                case 'custom-header' :
 
1723
                        if ( ! did_action( 'wp_loaded' ) )
 
1724
                                break;
 
1725
                        $support = get_theme_support( 'custom-header' );
 
1726
                        if ( $support[0]['wp-head-callback'] )
 
1727
                                remove_action( 'wp_head', $support[0]['wp-head-callback'] );
 
1728
                        remove_action( 'admin_menu', array( $GLOBALS['custom_image_header'], 'init' ) );
 
1729
                        unset( $GLOBALS['custom_image_header'] );
 
1730
                        break;
 
1731
 
 
1732
                case 'custom-background' :
 
1733
                        if ( ! did_action( 'wp_loaded' ) )
 
1734
                                break;
 
1735
                        $support = get_theme_support( 'custom-background' );
 
1736
                        remove_action( 'wp_head', $support[0]['wp-head-callback'] );
 
1737
                        remove_action( 'admin_menu', array( $GLOBALS['custom_background'], 'init' ) );
 
1738
                        unset( $GLOBALS['custom_background'] );
 
1739
                        break;
 
1740
        }
 
1741
 
 
1742
        unset( $_wp_theme_features[ $feature ] );
 
1743
        return true;
 
1744
}
 
1745
 
 
1746
/**
 
1747
 * Checks a theme's support for a given feature
 
1748
 *
 
1749
 * @since 2.9.0
 
1750
 * @param string $feature the feature being checked
 
1751
 * @return boolean
 
1752
 */
 
1753
function current_theme_supports( $feature ) {
 
1754
        global $_wp_theme_features;
 
1755
 
 
1756
        if ( 'custom-header-uploads' == $feature )
 
1757
                return current_theme_supports( 'custom-header', 'uploads' );
 
1758
 
 
1759
        if ( !isset( $_wp_theme_features[$feature] ) )
 
1760
                return false;
 
1761
 
 
1762
        // If no args passed then no extra checks need be performed
 
1763
        if ( func_num_args() <= 1 )
 
1764
                return true;
 
1765
 
 
1766
        $args = array_slice( func_get_args(), 1 );
 
1767
 
 
1768
        switch ( $feature ) {
 
1769
                case 'post-thumbnails':
 
1770
                        // post-thumbnails can be registered for only certain content/post types by passing
 
1771
                        // an array of types to add_theme_support(). If no array was passed, then
 
1772
                        // any type is accepted
 
1773
                        if ( true === $_wp_theme_features[$feature] )  // Registered for all types
 
1774
                                return true;
 
1775
                        $content_type = $args[0];
 
1776
                        return in_array( $content_type, $_wp_theme_features[$feature][0] );
 
1777
 
 
1778
                case 'html5':
 
1779
                case 'post-formats':
 
1780
                        // specific post formats can be registered by passing an array of types to
 
1781
                        // add_theme_support()
 
1782
 
 
1783
                        // Specific areas of HTML5 support *must* be passed via an array to add_theme_support()
 
1784
 
 
1785
                        $type = $args[0];
 
1786
                        return in_array( $type, $_wp_theme_features[$feature][0] );
 
1787
 
 
1788
                case 'custom-header':
 
1789
                case 'custom-background' :
 
1790
                        // specific custom header and background capabilities can be registered by passing
 
1791
                        // an array to add_theme_support()
 
1792
                        $header_support = $args[0];
 
1793
                        return ( isset( $_wp_theme_features[$feature][0][$header_support] ) && $_wp_theme_features[$feature][0][$header_support] );
 
1794
        }
 
1795
 
 
1796
        /**
 
1797
         * Filter whether the current theme supports a specific feature.
 
1798
         *
 
1799
         * The dynamic portion of the hook name, $feature, refers to
 
1800
         * the specific theme feature. Possible values include 'post-formats',
 
1801
         * 'post-thumbnails', 'custom-background', 'custom-header', 'menus',
 
1802
         * 'automatic-feed-links', and 'html5'.
 
1803
         *
 
1804
         * @since 3.4.0
 
1805
         *
 
1806
         * @param bool   true     Whether the current theme supports the given feature. Default true.
 
1807
         * @param array  $args    Array of arguments for the feature.
 
1808
         * @param string $feature The theme feature.
 
1809
         */
 
1810
        return apply_filters( "current_theme_supports-{$feature}", true, $args, $_wp_theme_features[$feature] );
 
1811
}
 
1812
 
 
1813
/**
 
1814
 * Checks a theme's support for a given feature before loading the functions which implement it.
 
1815
 *
 
1816
 * @since 2.9.0
 
1817
 *
 
1818
 * @param string $feature The feature being checked.
 
1819
 * @param string $include Path to the file.
 
1820
 * @return bool True if the current theme supports the supplied feature, false otherwise.
 
1821
 */
 
1822
function require_if_theme_supports( $feature, $include ) {
 
1823
        if ( current_theme_supports( $feature ) ) {
 
1824
                require ( $include );
 
1825
                return true;
 
1826
        }
 
1827
        return false;
 
1828
}
 
1829
 
 
1830
/**
 
1831
 * Checks an attachment being deleted to see if it's a header or background image.
 
1832
 *
 
1833
 * If true it removes the theme modification which would be pointing at the deleted
 
1834
 * attachment
 
1835
 *
 
1836
 * @access private
 
1837
 * @since 3.0.0
 
1838
 * @param int $id the attachment id
 
1839
 */
 
1840
function _delete_attachment_theme_mod( $id ) {
 
1841
        $attachment_image = wp_get_attachment_url( $id );
 
1842
        $header_image = get_header_image();
 
1843
        $background_image = get_background_image();
 
1844
 
 
1845
        if ( $header_image && $header_image == $attachment_image )
 
1846
                remove_theme_mod( 'header_image' );
 
1847
 
 
1848
        if ( $background_image && $background_image == $attachment_image )
 
1849
                remove_theme_mod( 'background_image' );
 
1850
}
 
1851
 
 
1852
add_action( 'delete_attachment', '_delete_attachment_theme_mod' );
 
1853
 
 
1854
/**
 
1855
 * Checks if a theme has been changed and runs 'after_switch_theme' hook on the next WP load
 
1856
 *
 
1857
 * @since 3.3.0
 
1858
 */
 
1859
function check_theme_switched() {
 
1860
        if ( $stylesheet = get_option( 'theme_switched' ) ) {
 
1861
                $old_theme = wp_get_theme( $stylesheet );
 
1862
 
 
1863
                // Prevent retrieve_widgets() from running since Customizer already called it up front
 
1864
                if ( get_option( 'theme_switched_via_customizer' ) ) {
 
1865
                        remove_action( 'after_switch_theme', '_wp_sidebars_changed' );
 
1866
                        update_option( 'theme_switched_via_customizer', false );
 
1867
                }
 
1868
 
 
1869
                if ( $old_theme->exists() ) {
 
1870
                        /**
 
1871
                         * Fires on the first WP load after a theme switch if the old theme still exists.
 
1872
                         *
 
1873
                         * This action fires multiple times and the parameters differs
 
1874
                         * according to the context, if the old theme exists or not.
 
1875
                         * If the old theme is missing, the parameter will be the slug
 
1876
                         * of the old theme.
 
1877
                         *
 
1878
                         * @since 3.3.0
 
1879
                         *
 
1880
                         * @param string   $old_name  Old theme name.
 
1881
                         * @param WP_Theme $old_theme WP_Theme instance of the old theme.
 
1882
                         */
 
1883
                        do_action( 'after_switch_theme', $old_theme->get( 'Name' ), $old_theme );
 
1884
                } else {
 
1885
                        /** This action is documented in wp-includes/theme.php */
 
1886
                        do_action( 'after_switch_theme', $stylesheet );
 
1887
                }
 
1888
 
 
1889
                update_option( 'theme_switched', false );
 
1890
        }
 
1891
}
 
1892
 
 
1893
/**
 
1894
 * Includes and instantiates the WP_Customize_Manager class.
 
1895
 *
 
1896
 * Fires when ?wp_customize=on or on wp-admin/customize.php.
 
1897
 *
 
1898
 * @since 3.4.0
 
1899
 */
 
1900
function _wp_customize_include() {
 
1901
        if ( ! ( ( isset( $_REQUEST['wp_customize'] ) && 'on' == $_REQUEST['wp_customize'] )
 
1902
                || ( is_admin() && 'customize.php' == basename( $_SERVER['PHP_SELF'] ) )
 
1903
        ) )
 
1904
                return;
 
1905
 
 
1906
        require( ABSPATH . WPINC . '/class-wp-customize-manager.php' );
 
1907
        // Init Customize class
 
1908
        $GLOBALS['wp_customize'] = new WP_Customize_Manager;
 
1909
}
 
1910
add_action( 'plugins_loaded', '_wp_customize_include' );
 
1911
 
 
1912
/**
 
1913
 * Adds settings for the customize-loader script.
 
1914
 *
 
1915
 * @since 3.4.0
 
1916
 */
 
1917
function _wp_customize_loader_settings() {
 
1918
        global $wp_scripts;
 
1919
 
 
1920
        $admin_origin = parse_url( admin_url() );
 
1921
        $home_origin  = parse_url( home_url() );
 
1922
        $cross_domain = ( strtolower( $admin_origin[ 'host' ] ) != strtolower( $home_origin[ 'host' ] ) );
 
1923
 
 
1924
        $browser = array(
 
1925
                'mobile' => wp_is_mobile(),
 
1926
                'ios'    => wp_is_mobile() && preg_match( '/iPad|iPod|iPhone/', $_SERVER['HTTP_USER_AGENT'] ),
 
1927
        );
 
1928
 
 
1929
        $settings = array(
 
1930
                'url'           => esc_url( admin_url( 'customize.php' ) ),
 
1931
                'isCrossDomain' => $cross_domain,
 
1932
                'browser'       => $browser,
 
1933
                'l10n'          => array(
 
1934
                        'saveAlert' => __( 'The changes you made will be lost if you navigate away from this page.' ),
 
1935
                ),
 
1936
        );
 
1937
 
 
1938
        $script = 'var _wpCustomizeLoaderSettings = ' . json_encode( $settings ) . ';';
 
1939
 
 
1940
        $data = $wp_scripts->get_data( 'customize-loader', 'data' );
 
1941
        if ( $data )
 
1942
                $script = "$data\n$script";
 
1943
 
 
1944
        $wp_scripts->add_data( 'customize-loader', 'data', $script );
 
1945
}
 
1946
add_action( 'admin_enqueue_scripts', '_wp_customize_loader_settings' );
 
1947
 
 
1948
/**
 
1949
 * Returns a URL to load the theme customizer.
 
1950
 *
 
1951
 * @since 3.4.0
 
1952
 *
 
1953
 * @param string $stylesheet Optional. Theme to customize. Defaults to current theme.
 
1954
 *      The theme's stylesheet will be urlencoded if necessary.
 
1955
 */
 
1956
function wp_customize_url( $stylesheet = null ) {
 
1957
        $url = admin_url( 'customize.php' );
 
1958
        if ( $stylesheet )
 
1959
                $url .= '?theme=' . urlencode( $stylesheet );
 
1960
        return esc_url( $url );
 
1961
}
 
1962
 
 
1963
/**
 
1964
 * Prints a script to check whether or not the customizer is supported,
 
1965
 * and apply either the no-customize-support or customize-support class
 
1966
 * to the body.
 
1967
 *
 
1968
 * This function MUST be called inside the body tag.
 
1969
 *
 
1970
 * Ideally, call this function immediately after the body tag is opened.
 
1971
 * This prevents a flash of unstyled content.
 
1972
 *
 
1973
 * It is also recommended that you add the "no-customize-support" class
 
1974
 * to the body tag by default.
 
1975
 *
 
1976
 * @since 3.4.0
 
1977
 */
 
1978
function wp_customize_support_script() {
 
1979
        $admin_origin = parse_url( admin_url() );
 
1980
        $home_origin  = parse_url( home_url() );
 
1981
        $cross_domain = ( strtolower( $admin_origin[ 'host' ] ) != strtolower( $home_origin[ 'host' ] ) );
 
1982
 
 
1983
        ?>
 
1984
        <script type="text/javascript">
 
1985
                (function() {
 
1986
                        var request, b = document.body, c = 'className', cs = 'customize-support', rcs = new RegExp('(^|\\s+)(no-)?'+cs+'(\\s+|$)');
 
1987
 
 
1988
<?php           if ( $cross_domain ): ?>
 
1989
                        request = (function(){ var xhr = new XMLHttpRequest(); return ('withCredentials' in xhr); })();
 
1990
<?php           else: ?>
 
1991
                        request = true;
 
1992
<?php           endif; ?>
 
1993
 
 
1994
                        b[c] = b[c].replace( rcs, ' ' );
 
1995
                        b[c] += ( window.postMessage && request ? ' ' : ' no-' ) + cs;
 
1996
                }());
 
1997
        </script>
 
1998
        <?php
 
1999
}
 
2000
 
 
2001
/**
 
2002
 * Whether the site is being previewed in the Customizer.
 
2003
 *
 
2004
 * @since 4.0.0
 
2005
 *
 
2006
 * @global WP_Customize_Manager $wp_customize Customizer instance.
 
2007
 *
 
2008
 * @return bool True if the site is being previewed in the Customizer, false otherwise.
 
2009
 */
 
2010
function is_customize_preview() {
 
2011
        global $wp_customize;
 
2012
 
 
2013
        return is_a( $wp_customize, 'WP_Customize_Manager' ) && $wp_customize->is_preview();
 
2014
}