Issues (2010)

Security Analysis    not enabled

This project does not seem to handle request data directly as such no vulnerable execution paths were found.

  Cross-Site Scripting
Cross-Site Scripting enables an attacker to inject code into the response of a web-request that is viewed by other users. It can for example be used to bypass access controls, or even to take over other users' accounts.
  File Exposure
File Exposure allows an attacker to gain access to local files that he should not be able to access. These files can for example include database credentials, or other configuration files.
  File Manipulation
File Manipulation enables an attacker to write custom data to files. This potentially leads to injection of arbitrary code on the server.
  Object Injection
Object Injection enables an attacker to inject an object into PHP code, and can lead to arbitrary code execution, file exposure, or file manipulation attacks.
  Code Injection
Code Injection enables an attacker to execute arbitrary code on the server.
  Response Splitting
Response Splitting can be used to send arbitrary responses.
  File Inclusion
File Inclusion enables an attacker to inject custom files into PHP's file loading mechanism, either explicitly passed to include, or for example via PHP's auto-loading mechanism.
  Command Injection
Command Injection enables an attacker to inject a shell command that is execute with the privileges of the web-server. This can be used to expose sensitive data, or gain access of your server.
  SQL Injection
SQL Injection enables an attacker to execute arbitrary SQL code on your database server gaining access to user data, or manipulating user data.
  XPath Injection
XPath Injection enables an attacker to modify the parts of XML document that are read. If that XML document is for example used for authentication, this can lead to further vulnerabilities similar to SQL Injection.
  LDAP Injection
LDAP Injection enables an attacker to inject LDAP statements potentially granting permission to run unauthorized queries, or modify content inside the LDAP tree.
  Header Injection
  Other Vulnerability
This category comprises other attack vectors such as manipulating the PHP runtime, loading custom extensions, freezing the runtime, or similar.
  Regex Injection
Regex Injection enables an attacker to execute arbitrary code in your PHP process.
  XML Injection
XML Injection enables an attacker to read files on your local filesystem including configuration files, or can be abused to freeze your web-server process.
  Variable Injection
Variable Injection enables an attacker to overwrite program variables with custom data, and can lead to further vulnerabilities.
Unfortunately, the security analysis is currently not available for your project. If you are a non-commercial open-source project, please contact support to gain access.

wp-includes/theme.php (10 issues)

Upgrade to new PHP Analysis Engine

These results are based on our legacy PHP analysis, consider migrating to our new PHP analysis engine instead. Learn more

Code
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
 * @global array $wp_theme_directories
18
 * @staticvar array $_themes
19
 *
20
 * @param array $args The search arguments. Optional.
21
 * - errors      mixed  True to return themes with errors, false to return themes without errors, null
22
 *                      to return all themes. Defaults to false.
23
 * - allowed     mixed  (Multisite) True to return only allowed themes for a site. False to return only
24
 *                      disallowed themes for a site. 'site' to return only site-allowed themes. 'network'
25
 *                      to return only network-allowed themes. Null to return all themes. Defaults to null.
26
 * - blog_id     int    (Multisite) The blog ID used to calculate which themes are allowed. Defaults to 0,
27
 *                      synonymous for the current blog.
28
 * @return array Array of WP_Theme objects.
29
 */
30
function wp_get_themes( $args = array() ) {
31
	global $wp_theme_directories;
32
33
	$defaults = array( 'errors' => false, 'allowed' => null, 'blog_id' => 0 );
34
	$args = wp_parse_args( $args, $defaults );
35
36
	$theme_directories = search_theme_directories();
37
38
	if ( count( $wp_theme_directories ) > 1 ) {
39
		// Make sure the current theme wins out, in case search_theme_directories() picks the wrong
40
		// one in the case of a conflict. (Normally, last registered theme root wins.)
41
		$current_theme = get_stylesheet();
42
		if ( isset( $theme_directories[ $current_theme ] ) ) {
43
			$root_of_current_theme = get_raw_theme_root( $current_theme );
44
			if ( ! in_array( $root_of_current_theme, $wp_theme_directories ) )
45
				$root_of_current_theme = WP_CONTENT_DIR . $root_of_current_theme;
46
			$theme_directories[ $current_theme ]['theme_root'] = $root_of_current_theme;
47
		}
48
	}
49
50
	if ( empty( $theme_directories ) )
51
		return array();
52
53
	if ( is_multisite() && null !== $args['allowed'] ) {
54
		$allowed = $args['allowed'];
55
		if ( 'network' === $allowed )
56
			$theme_directories = array_intersect_key( $theme_directories, WP_Theme::get_allowed_on_network() );
57
		elseif ( 'site' === $allowed )
58
			$theme_directories = array_intersect_key( $theme_directories, WP_Theme::get_allowed_on_site( $args['blog_id'] ) );
59
		elseif ( $allowed )
60
			$theme_directories = array_intersect_key( $theme_directories, WP_Theme::get_allowed( $args['blog_id'] ) );
61
		else
62
			$theme_directories = array_diff_key( $theme_directories, WP_Theme::get_allowed( $args['blog_id'] ) );
63
	}
64
65
	$themes = array();
66
	static $_themes = array();
67
68
	foreach ( $theme_directories as $theme => $theme_root ) {
69
		if ( isset( $_themes[ $theme_root['theme_root'] . '/' . $theme ] ) )
70
			$themes[ $theme ] = $_themes[ $theme_root['theme_root'] . '/' . $theme ];
71
		else
72
			$themes[ $theme ] = $_themes[ $theme_root['theme_root'] . '/' . $theme ] = new WP_Theme( $theme, $theme_root['theme_root'] );
73
	}
74
75
	if ( null !== $args['errors'] ) {
76
		foreach ( $themes as $theme => $wp_theme ) {
77
			if ( $wp_theme->errors() != $args['errors'] )
78
				unset( $themes[ $theme ] );
79
		}
80
	}
81
82
	return $themes;
83
}
84
85
/**
86
 * Gets a WP_Theme object for a theme.
87
 *
88
 * @since 3.4.0
89
 *
90
 * @global array $wp_theme_directories
91
 *
92
 * @param string $stylesheet Directory name for the theme. Optional. Defaults to current theme.
93
 * @param string $theme_root Absolute path of the theme root to look in. Optional. If not specified, get_raw_theme_root()
94
 * 	                         is used to calculate the theme root for the $stylesheet provided (or current theme).
95
 * @return WP_Theme Theme object. Be sure to check the object's exists() method if you need to confirm the theme's existence.
96
 */
97
function wp_get_theme( $stylesheet = null, $theme_root = null ) {
98
	global $wp_theme_directories;
99
100
	if ( empty( $stylesheet ) )
101
		$stylesheet = get_stylesheet();
102
103 View Code Duplication
	if ( empty( $theme_root ) ) {
104
		$theme_root = get_raw_theme_root( $stylesheet );
105
		if ( false === $theme_root )
106
			$theme_root = WP_CONTENT_DIR . '/themes';
107
		elseif ( ! in_array( $theme_root, (array) $wp_theme_directories ) )
108
			$theme_root = WP_CONTENT_DIR . $theme_root;
109
	}
110
111
	return new WP_Theme( $stylesheet, $theme_root );
112
}
113
114
/**
115
 * Clears the cache held by get_theme_roots() and WP_Theme.
116
 *
117
 * @since 3.5.0
118
 * @param bool $clear_update_cache Whether to clear the Theme updates cache
119
 */
120
function wp_clean_themes_cache( $clear_update_cache = true ) {
121
	if ( $clear_update_cache )
122
		delete_site_transient( 'update_themes' );
123
	search_theme_directories( true );
124
	foreach ( wp_get_themes( array( 'errors' => null ) ) as $theme )
125
		$theme->cache_delete();
126
}
127
128
/**
129
 * Whether a child theme is in use.
130
 *
131
 * @since 3.0.0
132
 *
133
 * @return bool true if a child theme is in use, false otherwise.
134
 **/
135
function is_child_theme() {
136
	return ( TEMPLATEPATH !== STYLESHEETPATH );
137
}
138
139
/**
140
 * Retrieve name of the current stylesheet.
141
 *
142
 * The theme name that the administrator has currently set the front end theme
143
 * as.
144
 *
145
 * For all intents and purposes, the template name and the stylesheet name are
146
 * going to be the same for most cases.
147
 *
148
 * @since 1.5.0
149
 *
150
 * @return string Stylesheet name.
151
 */
152
function get_stylesheet() {
153
	/**
154
	 * Filters the name of current stylesheet.
155
	 *
156
	 * @since 1.5.0
157
	 *
158
	 * @param string $stylesheet Name of the current stylesheet.
159
	 */
160
	return apply_filters( 'stylesheet', get_option( 'stylesheet' ) );
161
}
162
163
/**
164
 * Retrieve stylesheet directory path for current theme.
165
 *
166
 * @since 1.5.0
167
 *
168
 * @return string Path to current theme directory.
169
 */
170
function get_stylesheet_directory() {
171
	$stylesheet = get_stylesheet();
172
	$theme_root = get_theme_root( $stylesheet );
173
	$stylesheet_dir = "$theme_root/$stylesheet";
174
175
	/**
176
	 * Filters the stylesheet directory path for current theme.
177
	 *
178
	 * @since 1.5.0
179
	 *
180
	 * @param string $stylesheet_dir Absolute path to the current theme.
181
	 * @param string $stylesheet     Directory name of the current theme.
182
	 * @param string $theme_root     Absolute path to themes directory.
183
	 */
184
	return apply_filters( 'stylesheet_directory', $stylesheet_dir, $stylesheet, $theme_root );
185
}
186
187
/**
188
 * Retrieve stylesheet directory URI.
189
 *
190
 * @since 1.5.0
191
 *
192
 * @return string
193
 */
194 View Code Duplication
function get_stylesheet_directory_uri() {
0 ignored issues
show
This function seems to be duplicated in your project.

Duplicated code is one of the most pungent code smells. If you need to duplicate the same code in three or more different places, we strongly encourage you to look into extracting the code into a single class or operation.

You can also find more detailed suggestions in the “Code” section of your repository.

Loading history...
195
	$stylesheet = str_replace( '%2F', '/', rawurlencode( get_stylesheet() ) );
196
	$theme_root_uri = get_theme_root_uri( $stylesheet );
197
	$stylesheet_dir_uri = "$theme_root_uri/$stylesheet";
198
199
	/**
200
	 * Filters the stylesheet directory URI.
201
	 *
202
	 * @since 1.5.0
203
	 *
204
	 * @param string $stylesheet_dir_uri Stylesheet directory URI.
205
	 * @param string $stylesheet         Name of the activated theme's directory.
206
	 * @param string $theme_root_uri     Themes root URI.
207
	 */
208
	return apply_filters( 'stylesheet_directory_uri', $stylesheet_dir_uri, $stylesheet, $theme_root_uri );
209
}
210
211
/**
212
 * Retrieves the URI of current theme stylesheet.
213
 *
214
 * The stylesheet file name is 'style.css' which is appended to the stylesheet directory URI path.
215
 * See get_stylesheet_directory_uri().
216
 *
217
 * @since 1.5.0
218
 *
219
 * @return string
220
 */
221
function get_stylesheet_uri() {
222
	$stylesheet_dir_uri = get_stylesheet_directory_uri();
223
	$stylesheet_uri = $stylesheet_dir_uri . '/style.css';
224
	/**
225
	 * Filters the URI of the current theme stylesheet.
226
	 *
227
	 * @since 1.5.0
228
	 *
229
	 * @param string $stylesheet_uri     Stylesheet URI for the current theme/child theme.
230
	 * @param string $stylesheet_dir_uri Stylesheet directory URI for the current theme/child theme.
231
	 */
232
	return apply_filters( 'stylesheet_uri', $stylesheet_uri, $stylesheet_dir_uri );
233
}
234
235
/**
236
 * Retrieves the localized stylesheet URI.
237
 *
238
 * The stylesheet directory for the localized stylesheet files are located, by
239
 * default, in the base theme directory. The name of the locale file will be the
240
 * locale followed by '.css'. If that does not exist, then the text direction
241
 * stylesheet will be checked for existence, for example 'ltr.css'.
242
 *
243
 * The theme may change the location of the stylesheet directory by either using
244
 * the {@see 'stylesheet_directory_uri'} or {@see 'locale_stylesheet_uri'} filters.
245
 *
246
 * If you want to change the location of the stylesheet files for the entire
247
 * WordPress workflow, then change the former. If you just have the locale in a
248
 * separate folder, then change the latter.
249
 *
250
 * @since 2.1.0
251
 *
252
 * @global WP_Locale $wp_locale
253
 *
254
 * @return string
255
 */
256
function get_locale_stylesheet_uri() {
257
	global $wp_locale;
258
	$stylesheet_dir_uri = get_stylesheet_directory_uri();
259
	$dir = get_stylesheet_directory();
260
	$locale = get_locale();
261
	if ( file_exists("$dir/$locale.css") )
262
		$stylesheet_uri = "$stylesheet_dir_uri/$locale.css";
263
	elseif ( !empty($wp_locale->text_direction) && file_exists("$dir/{$wp_locale->text_direction}.css") )
264
		$stylesheet_uri = "$stylesheet_dir_uri/{$wp_locale->text_direction}.css";
265
	else
266
		$stylesheet_uri = '';
267
	/**
268
	 * Filters the localized stylesheet URI.
269
	 *
270
	 * @since 2.1.0
271
	 *
272
	 * @param string $stylesheet_uri     Localized stylesheet URI.
273
	 * @param string $stylesheet_dir_uri Stylesheet directory URI.
274
	 */
275
	return apply_filters( 'locale_stylesheet_uri', $stylesheet_uri, $stylesheet_dir_uri );
276
}
277
278
/**
279
 * Retrieve name of the current theme.
280
 *
281
 * @since 1.5.0
282
 *
283
 * @return string Template name.
284
 */
285
function get_template() {
286
	/**
287
	 * Filters the name of the current theme.
288
	 *
289
	 * @since 1.5.0
290
	 *
291
	 * @param string $template Current theme's directory name.
292
	 */
293
	return apply_filters( 'template', get_option( 'template' ) );
294
}
295
296
/**
297
 * Retrieve current theme directory.
298
 *
299
 * @since 1.5.0
300
 *
301
 * @return string Template directory path.
302
 */
303
function get_template_directory() {
304
	$template = get_template();
305
	$theme_root = get_theme_root( $template );
306
	$template_dir = "$theme_root/$template";
307
308
	/**
309
	 * Filters the current theme directory path.
310
	 *
311
	 * @since 1.5.0
312
	 *
313
	 * @param string $template_dir The URI of the current theme directory.
314
	 * @param string $template     Directory name of the current theme.
315
	 * @param string $theme_root   Absolute path to the themes directory.
316
	 */
317
	return apply_filters( 'template_directory', $template_dir, $template, $theme_root );
318
}
319
320
/**
321
 * Retrieve theme directory URI.
322
 *
323
 * @since 1.5.0
324
 *
325
 * @return string Template directory URI.
326
 */
327 View Code Duplication
function get_template_directory_uri() {
0 ignored issues
show
This function seems to be duplicated in your project.

Duplicated code is one of the most pungent code smells. If you need to duplicate the same code in three or more different places, we strongly encourage you to look into extracting the code into a single class or operation.

You can also find more detailed suggestions in the “Code” section of your repository.

Loading history...
328
	$template = str_replace( '%2F', '/', rawurlencode( get_template() ) );
329
	$theme_root_uri = get_theme_root_uri( $template );
330
	$template_dir_uri = "$theme_root_uri/$template";
331
332
	/**
333
	 * Filters the current theme directory URI.
334
	 *
335
	 * @since 1.5.0
336
	 *
337
	 * @param string $template_dir_uri The URI of the current theme directory.
338
	 * @param string $template         Directory name of the current theme.
339
	 * @param string $theme_root_uri   The themes root URI.
340
	 */
341
	return apply_filters( 'template_directory_uri', $template_dir_uri, $template, $theme_root_uri );
342
}
343
344
/**
345
 * Retrieve theme roots.
346
 *
347
 * @since 2.9.0
348
 *
349
 * @global array $wp_theme_directories
350
 *
351
 * @return array|string An array of theme roots keyed by template/stylesheet or a single theme root if all themes have the same root.
352
 */
353
function get_theme_roots() {
354
	global $wp_theme_directories;
355
356
	if ( count($wp_theme_directories) <= 1 )
357
		return '/themes';
358
359
	$theme_roots = get_site_transient( 'theme_roots' );
360
	if ( false === $theme_roots ) {
361
		search_theme_directories( true ); // Regenerate the transient.
362
		$theme_roots = get_site_transient( 'theme_roots' );
363
	}
364
	return $theme_roots;
365
}
366
367
/**
368
 * Register a directory that contains themes.
369
 *
370
 * @since 2.9.0
371
 *
372
 * @global array $wp_theme_directories
373
 *
374
 * @param string $directory Either the full filesystem path to a theme folder or a folder within WP_CONTENT_DIR
375
 * @return bool
376
 */
377
function register_theme_directory( $directory ) {
378
	global $wp_theme_directories;
379
380
	if ( ! file_exists( $directory ) ) {
381
		// Try prepending as the theme directory could be relative to the content directory
382
		$directory = WP_CONTENT_DIR . '/' . $directory;
383
		// If this directory does not exist, return and do not register
384
		if ( ! file_exists( $directory ) ) {
385
			return false;
386
		}
387
	}
388
389
	if ( ! is_array( $wp_theme_directories ) ) {
390
		$wp_theme_directories = array();
391
	}
392
393
	$untrailed = untrailingslashit( $directory );
394
	if ( ! empty( $untrailed ) && ! in_array( $untrailed, $wp_theme_directories ) ) {
395
		$wp_theme_directories[] = $untrailed;
396
	}
397
398
	return true;
399
}
400
401
/**
402
 * Search all registered theme directories for complete and valid themes.
403
 *
404
 * @since 2.9.0
405
 *
406
 * @global array $wp_theme_directories
407
 * @staticvar array $found_themes
408
 *
409
 * @param bool $force Optional. Whether to force a new directory scan. Defaults to false.
410
 * @return array|false Valid themes found
411
 */
412
function search_theme_directories( $force = false ) {
413
	global $wp_theme_directories;
414
	static $found_themes = null;
415
416
	if ( empty( $wp_theme_directories ) )
417
		return false;
418
419
	if ( ! $force && isset( $found_themes ) )
420
		return $found_themes;
421
422
	$found_themes = array();
423
424
	$wp_theme_directories = (array) $wp_theme_directories;
425
	$relative_theme_roots = array();
426
427
	// Set up maybe-relative, maybe-absolute array of theme directories.
428
	// We always want to return absolute, but we need to cache relative
429
	// to use in get_theme_root().
430
	foreach ( $wp_theme_directories as $theme_root ) {
431
		if ( 0 === strpos( $theme_root, WP_CONTENT_DIR ) )
432
			$relative_theme_roots[ str_replace( WP_CONTENT_DIR, '', $theme_root ) ] = $theme_root;
433
		else
434
			$relative_theme_roots[ $theme_root ] = $theme_root;
435
	}
436
437
	/**
438
	 * Filters whether to get the cache of the registered theme directories.
439
	 *
440
	 * @since 3.4.0
441
	 *
442
	 * @param bool   $cache_expiration Whether to get the cache of the theme directories. Default false.
443
	 * @param string $cache_directory  Directory to be searched for the cache.
444
	 */
445
	if ( $cache_expiration = apply_filters( 'wp_cache_themes_persistently', false, 'search_theme_directories' ) ) {
446
		$cached_roots = get_site_transient( 'theme_roots' );
447
		if ( is_array( $cached_roots ) ) {
448
			foreach ( $cached_roots as $theme_dir => $theme_root ) {
449
				// A cached theme root is no longer around, so skip it.
450
				if ( ! isset( $relative_theme_roots[ $theme_root ] ) )
451
					continue;
452
				$found_themes[ $theme_dir ] = array(
453
					'theme_file' => $theme_dir . '/style.css',
454
					'theme_root' => $relative_theme_roots[ $theme_root ], // Convert relative to absolute.
455
				);
456
			}
457
			return $found_themes;
458
		}
459
		if ( ! is_int( $cache_expiration ) )
460
			$cache_expiration = 1800; // half hour
461
	} else {
462
		$cache_expiration = 1800; // half hour
463
	}
464
465
	/* Loop the registered theme directories and extract all themes */
466
	foreach ( $wp_theme_directories as $theme_root ) {
467
468
		// Start with directories in the root of the current theme directory.
469
		$dirs = @ scandir( $theme_root );
470
		if ( ! $dirs ) {
471
			trigger_error( "$theme_root is not readable", E_USER_NOTICE );
472
			continue;
473
		}
474
		foreach ( $dirs as $dir ) {
475 View Code Duplication
			if ( ! is_dir( $theme_root . '/' . $dir ) || $dir[0] == '.' || $dir == 'CVS' )
476
				continue;
477
			if ( file_exists( $theme_root . '/' . $dir . '/style.css' ) ) {
478
				// wp-content/themes/a-single-theme
479
				// wp-content/themes is $theme_root, a-single-theme is $dir
480
				$found_themes[ $dir ] = array(
481
					'theme_file' => $dir . '/style.css',
482
					'theme_root' => $theme_root,
483
				);
484
			} else {
485
				$found_theme = false;
486
				// wp-content/themes/a-folder-of-themes/*
487
				// wp-content/themes is $theme_root, a-folder-of-themes is $dir, then themes are $sub_dirs
488
				$sub_dirs = @ scandir( $theme_root . '/' . $dir );
489
				if ( ! $sub_dirs ) {
490
					trigger_error( "$theme_root/$dir is not readable", E_USER_NOTICE );
491
					continue;
492
				}
493
				foreach ( $sub_dirs as $sub_dir ) {
494 View Code Duplication
					if ( ! is_dir( $theme_root . '/' . $dir . '/' . $sub_dir ) || $dir[0] == '.' || $dir == 'CVS' )
495
						continue;
496
					if ( ! file_exists( $theme_root . '/' . $dir . '/' . $sub_dir . '/style.css' ) )
497
						continue;
498
					$found_themes[ $dir . '/' . $sub_dir ] = array(
499
						'theme_file' => $dir . '/' . $sub_dir . '/style.css',
500
						'theme_root' => $theme_root,
501
					);
502
					$found_theme = true;
503
				}
504
				// Never mind the above, it's just a theme missing a style.css.
505
				// Return it; WP_Theme will catch the error.
506
				if ( ! $found_theme )
507
					$found_themes[ $dir ] = array(
508
						'theme_file' => $dir . '/style.css',
509
						'theme_root' => $theme_root,
510
					);
511
			}
512
		}
513
	}
514
515
	asort( $found_themes );
516
517
	$theme_roots = array();
518
	$relative_theme_roots = array_flip( $relative_theme_roots );
519
520
	foreach ( $found_themes as $theme_dir => $theme_data ) {
521
		$theme_roots[ $theme_dir ] = $relative_theme_roots[ $theme_data['theme_root'] ]; // Convert absolute to relative.
522
	}
523
524
	if ( $theme_roots != get_site_transient( 'theme_roots' ) )
525
		set_site_transient( 'theme_roots', $theme_roots, $cache_expiration );
526
527
	return $found_themes;
528
}
529
530
/**
531
 * Retrieve path to themes directory.
532
 *
533
 * Does not have trailing slash.
534
 *
535
 * @since 1.5.0
536
 *
537
 * @global array $wp_theme_directories
538
 *
539
 * @param string $stylesheet_or_template The stylesheet or template name of the theme
540
 * @return string Theme path.
541
 */
542
function get_theme_root( $stylesheet_or_template = false ) {
543
	global $wp_theme_directories;
544
545 View Code Duplication
	if ( $stylesheet_or_template && $theme_root = get_raw_theme_root( $stylesheet_or_template ) ) {
546
		// Always prepend WP_CONTENT_DIR unless the root currently registered as a theme directory.
547
		// This gives relative theme roots the benefit of the doubt when things go haywire.
548
		if ( ! in_array( $theme_root, (array) $wp_theme_directories ) )
549
			$theme_root = WP_CONTENT_DIR . $theme_root;
550
	} else {
551
		$theme_root = WP_CONTENT_DIR . '/themes';
552
	}
553
554
	/**
555
	 * Filters the absolute path to the themes directory.
556
	 *
557
	 * @since 1.5.0
558
	 *
559
	 * @param string $theme_root Absolute path to themes directory.
560
	 */
561
	return apply_filters( 'theme_root', $theme_root );
562
}
563
564
/**
565
 * Retrieve URI for themes directory.
566
 *
567
 * Does not have trailing slash.
568
 *
569
 * @since 1.5.0
570
 *
571
 * @global array $wp_theme_directories
572
 *
573
 * @param string $stylesheet_or_template Optional. The stylesheet or template name of the theme.
574
 * 	                                     Default is to leverage the main theme root.
575
 * @param string $theme_root             Optional. The theme root for which calculations will be based, preventing
576
 * 	                                     the need for a get_raw_theme_root() call.
577
 * @return string Themes URI.
578
 */
579
function get_theme_root_uri( $stylesheet_or_template = false, $theme_root = false ) {
580
	global $wp_theme_directories;
581
582
	if ( $stylesheet_or_template && ! $theme_root )
0 ignored issues
show
Bug Best Practice introduced by
The expression $theme_root of type false|string is loosely compared to false; this is ambiguous if the string can be empty. You might want to explicitly use === false instead.

In PHP, under loose comparison (like ==, or !=, or switch conditions), values of different types might be equal.

For string values, the empty string '' is a special case, in particular the following results might be unexpected:

''   == false // true
''   == null  // true
'ab' == false // false
'ab' == null  // false

// It is often better to use strict comparison
'' === false // false
'' === null  // false
Loading history...
583
		$theme_root = get_raw_theme_root( $stylesheet_or_template );
584
585
	if ( $stylesheet_or_template && $theme_root ) {
586
		if ( in_array( $theme_root, (array) $wp_theme_directories ) ) {
587
			// Absolute path. Make an educated guess. YMMV -- but note the filter below.
588
			if ( 0 === strpos( $theme_root, WP_CONTENT_DIR ) )
589
				$theme_root_uri = content_url( str_replace( WP_CONTENT_DIR, '', $theme_root ) );
590
			elseif ( 0 === strpos( $theme_root, ABSPATH ) )
591
				$theme_root_uri = site_url( str_replace( ABSPATH, '', $theme_root ) );
592
			elseif ( 0 === strpos( $theme_root, WP_PLUGIN_DIR ) || 0 === strpos( $theme_root, WPMU_PLUGIN_DIR ) )
593
				$theme_root_uri = plugins_url( basename( $theme_root ), $theme_root );
594
			else
595
				$theme_root_uri = $theme_root;
596
		} else {
597
			$theme_root_uri = content_url( $theme_root );
598
		}
599
	} else {
600
		$theme_root_uri = content_url( 'themes' );
601
	}
602
603
	/**
604
	 * Filters the URI for themes directory.
605
	 *
606
	 * @since 1.5.0
607
	 *
608
	 * @param string $theme_root_uri         The URI for themes directory.
609
	 * @param string $siteurl                WordPress web address which is set in General Options.
610
	 * @param string $stylesheet_or_template Stylesheet or template name of the theme.
611
	 */
612
	return apply_filters( 'theme_root_uri', $theme_root_uri, get_option( 'siteurl' ), $stylesheet_or_template );
613
}
614
615
/**
616
 * Get the raw theme root relative to the content directory with no filters applied.
617
 *
618
 * @since 3.1.0
619
 *
620
 * @global array $wp_theme_directories
621
 *
622
 * @param string $stylesheet_or_template The stylesheet or template name of the theme
623
 * @param bool   $skip_cache             Optional. Whether to skip the cache.
624
 *                                       Defaults to false, meaning the cache is used.
625
 * @return string Theme root
626
 */
627
function get_raw_theme_root( $stylesheet_or_template, $skip_cache = false ) {
628
	global $wp_theme_directories;
629
630
	if ( count($wp_theme_directories) <= 1 )
631
		return '/themes';
632
633
	$theme_root = false;
634
635
	// If requesting the root for the current theme, consult options to avoid calling get_theme_roots()
636
	if ( ! $skip_cache ) {
637
		if ( get_option('stylesheet') == $stylesheet_or_template )
638
			$theme_root = get_option('stylesheet_root');
639
		elseif ( get_option('template') == $stylesheet_or_template )
640
			$theme_root = get_option('template_root');
641
	}
642
643
	if ( empty($theme_root) ) {
644
		$theme_roots = get_theme_roots();
645
		if ( !empty($theme_roots[$stylesheet_or_template]) )
646
			$theme_root = $theme_roots[$stylesheet_or_template];
647
	}
648
649
	return $theme_root;
650
}
651
652
/**
653
 * Display localized stylesheet link element.
654
 *
655
 * @since 2.1.0
656
 */
657
function locale_stylesheet() {
658
	$stylesheet = get_locale_stylesheet_uri();
659
	if ( empty($stylesheet) )
660
		return;
661
	echo '<link rel="stylesheet" href="' . $stylesheet . '" type="text/css" media="screen" />';
662
}
663
664
/**
665
 * Switches the theme.
666
 *
667
 * Accepts one argument: $stylesheet of the theme. It also accepts an additional function signature
668
 * of two arguments: $template then $stylesheet. This is for backward compatibility.
669
 *
670
 * @since 2.5.0
671
 *
672
 * @global array                $wp_theme_directories
673
 * @global WP_Customize_Manager $wp_customize
674
 * @global array                $sidebars_widgets
675
 *
676
 * @param string $stylesheet Stylesheet name
677
 */
678
function switch_theme( $stylesheet ) {
679
	global $wp_theme_directories, $wp_customize, $sidebars_widgets;
680
681
	$_sidebars_widgets = null;
682
	if ( 'wp_ajax_customize_save' === current_action() ) {
683
		$_sidebars_widgets = $wp_customize->post_value( $wp_customize->get_setting( 'old_sidebars_widgets_data' ) );
684
	} elseif ( is_array( $sidebars_widgets ) ) {
685
		$_sidebars_widgets = $sidebars_widgets;
686
	}
687
688
	if ( is_array( $_sidebars_widgets ) ) {
689
		set_theme_mod( 'sidebars_widgets', array( 'time' => time(), 'data' => $_sidebars_widgets ) );
690
	}
691
692
	$nav_menu_locations = get_theme_mod( 'nav_menu_locations' );
693
694
	if ( func_num_args() > 1 ) {
695
		$stylesheet = func_get_arg( 1 );
696
	}
697
698
	$old_theme = wp_get_theme();
699
	$new_theme = wp_get_theme( $stylesheet );
700
	$template  = $new_theme->get_template();
701
702
	update_option( 'template', $template );
703
	update_option( 'stylesheet', $stylesheet );
704
705
	if ( count( $wp_theme_directories ) > 1 ) {
706
		update_option( 'template_root', get_raw_theme_root( $template, true ) );
707
		update_option( 'stylesheet_root', get_raw_theme_root( $stylesheet, true ) );
708
	} else {
709
		delete_option( 'template_root' );
710
		delete_option( 'stylesheet_root' );
711
	}
712
713
	$new_name  = $new_theme->get('Name');
714
715
	update_option( 'current_theme', $new_name );
716
717
	// Migrate from the old mods_{name} option to theme_mods_{slug}.
718
	if ( is_admin() && false === get_option( 'theme_mods_' . $stylesheet ) ) {
719
		$default_theme_mods = (array) get_option( 'mods_' . $new_name );
720
		if ( ! empty( $nav_menu_locations ) && empty( $default_theme_mods['nav_menu_locations'] ) ) {
721
			$default_theme_mods['nav_menu_locations'] = $nav_menu_locations;
722
		}
723
		add_option( "theme_mods_$stylesheet", $default_theme_mods );
724
	} else {
725
		/*
726
		 * Since retrieve_widgets() is called when initializing a theme in the Customizer,
727
		 * we need to remove the theme mods to avoid overwriting changes made via
728
		 * the Customizer when accessing wp-admin/widgets.php.
729
		 */
730
		if ( 'wp_ajax_customize_save' === current_action() ) {
731
			remove_theme_mod( 'sidebars_widgets' );
732
		}
733
734
		if ( ! empty( $nav_menu_locations ) ) {
735
			$nav_mods = get_theme_mod( 'nav_menu_locations' );
736
			if ( empty( $nav_mods ) ) {
737
				set_theme_mod( 'nav_menu_locations', $nav_menu_locations );
738
			}
739
		}
740
	}
741
742
	update_option( 'theme_switched', $old_theme->get_stylesheet() );
743
744
	/**
745
	 * Fires after the theme is switched.
746
	 *
747
	 * @since 1.5.0
748
	 * @since 4.5.0 Introduced the `$old_theme` parameter.
749
	 *
750
	 * @param string   $new_name  Name of the new theme.
751
	 * @param WP_Theme $new_theme WP_Theme instance of the new theme.
752
	 * @param WP_Theme $old_theme WP_Theme instance of the old theme.
753
	 */
754
	do_action( 'switch_theme', $new_name, $new_theme, $old_theme );
755
}
756
757
/**
758
 * Checks that current theme files 'index.php' and 'style.css' exists.
759
 *
760
 * Does not initially check the default theme, which is the fallback and should always exist.
761
 * But if it doesn't exist, it'll fall back to the latest core default theme that does exist.
762
 * Will switch theme to the fallback theme if current theme does not validate.
763
 *
764
 * You can use the {@see 'validate_current_theme'} filter to return false to
765
 * disable this functionality.
766
 *
767
 * @since 1.5.0
768
 * @see WP_DEFAULT_THEME
769
 *
770
 * @return bool
771
 */
772
function validate_current_theme() {
773
	/**
774
	 * Filters whether to validate the current theme.
775
	 *
776
	 * @since 2.7.0
777
	 *
778
	 * @param bool $validate Whether to validate the current theme. Default true.
779
	 */
780
	if ( wp_installing() || ! apply_filters( 'validate_current_theme', true ) )
781
		return true;
782
783
	if ( ! file_exists( get_template_directory() . '/index.php' ) ) {
0 ignored issues
show
This if statement is empty and can be removed.

This check looks for the bodies of if statements that have no statements or where all statements have been commented out. This may be the result of changes for debugging or the code may simply be obsolete.

These if bodies can be removed. If you have an empty if but statements in the else branch, consider inverting the condition.

if (rand(1, 6) > 3) {
//print "Check failed";
} else {
    print "Check succeeded";
}

could be turned into

if (rand(1, 6) <= 3) {
    print "Check succeeded";
}

This is much more concise to read.

Loading history...
784
		// Invalid.
785
	} elseif ( ! file_exists( get_template_directory() . '/style.css' ) ) {
0 ignored issues
show
This elseif statement is empty, and could be removed.

This check looks for the bodies of elseif statements that have no statements or where all statements have been commented out. This may be the result of changes for debugging or the code may simply be obsolete.

These elseif bodies can be removed. If you have an empty elseif but statements in the else branch, consider inverting the condition.

Loading history...
786
		// Invalid.
787
	} elseif ( is_child_theme() && ! file_exists( get_stylesheet_directory() . '/style.css' ) ) {
0 ignored issues
show
This elseif statement is empty, and could be removed.

This check looks for the bodies of elseif statements that have no statements or where all statements have been commented out. This may be the result of changes for debugging or the code may simply be obsolete.

These elseif bodies can be removed. If you have an empty elseif but statements in the else branch, consider inverting the condition.

Loading history...
788
		// Invalid.
789
	} else {
790
		// Valid.
791
		return true;
792
	}
793
794
	$default = wp_get_theme( WP_DEFAULT_THEME );
795
	if ( $default->exists() ) {
796
		switch_theme( WP_DEFAULT_THEME );
797
		return false;
798
	}
799
800
	/**
801
	 * If we're in an invalid state but WP_DEFAULT_THEME doesn't exist,
802
	 * switch to the latest core default theme that's installed.
803
	 * If it turns out that this latest core default theme is our current
804
	 * theme, then there's nothing we can do about that, so we have to bail,
805
	 * rather than going into an infinite loop. (This is why there are
806
	 * checks against WP_DEFAULT_THEME above, also.) We also can't do anything
807
	 * if it turns out there is no default theme installed. (That's `false`.)
808
	 */
809
	$default = WP_Theme::get_core_default_theme();
810
	if ( false === $default || get_stylesheet() == $default->get_stylesheet() ) {
811
		return true;
812
	}
813
814
	switch_theme( $default->get_stylesheet() );
815
	return false;
816
}
817
818
/**
819
 * Retrieve all theme modifications.
820
 *
821
 * @since 3.1.0
822
 *
823
 * @return array|void Theme modifications.
824
 */
825
function get_theme_mods() {
826
	$theme_slug = get_option( 'stylesheet' );
827
	$mods = get_option( "theme_mods_$theme_slug" );
828
	if ( false === $mods ) {
829
		$theme_name = get_option( 'current_theme' );
830
		if ( false === $theme_name )
831
			$theme_name = wp_get_theme()->get('Name');
832
		$mods = get_option( "mods_$theme_name" ); // Deprecated location.
833
		if ( is_admin() && false !== $mods ) {
834
			update_option( "theme_mods_$theme_slug", $mods );
835
			delete_option( "mods_$theme_name" );
836
		}
837
	}
838
	return $mods;
839
}
840
841
/**
842
 * Retrieve theme modification value for the current theme.
843
 *
844
 * If the modification name does not exist, then the $default will be passed
845
 * through {@link https://secure.php.net/sprintf sprintf()} PHP function with the first
846
 * string the template directory URI and the second string the stylesheet
847
 * directory URI.
848
 *
849
 * @since 2.1.0
850
 *
851
 * @param string      $name    Theme modification name.
852
 * @param bool|string $default
853
 * @return string
854
 */
855
function get_theme_mod( $name, $default = false ) {
856
	$mods = get_theme_mods();
857
858
	if ( isset( $mods[$name] ) ) {
859
		/**
860
		 * Filters the theme modification, or 'theme_mod', value.
861
		 *
862
		 * The dynamic portion of the hook name, `$name`, refers to
863
		 * the key name of the modification array. For example,
864
		 * 'header_textcolor', 'header_image', and so on depending
865
		 * on the theme options.
866
		 *
867
		 * @since 2.2.0
868
		 *
869
		 * @param string $current_mod The value of the current theme modification.
870
		 */
871
		return apply_filters( "theme_mod_{$name}", $mods[$name] );
872
	}
873
874
	if ( is_string( $default ) )
875
		$default = sprintf( $default, get_template_directory_uri(), get_stylesheet_directory_uri() );
876
877
	/** This filter is documented in wp-includes/theme.php */
878
	return apply_filters( "theme_mod_{$name}", $default );
879
}
880
881
/**
882
 * Update theme modification value for the current theme.
883
 *
884
 * @since 2.1.0
885
 *
886
 * @param string $name  Theme modification name.
887
 * @param mixed  $value Theme modification value.
888
 */
889
function set_theme_mod( $name, $value ) {
890
	$mods = get_theme_mods();
891
	$old_value = isset( $mods[ $name ] ) ? $mods[ $name ] : false;
892
893
	/**
894
	 * Filters the theme mod value on save.
895
	 *
896
	 * The dynamic portion of the hook name, `$name`, refers to the key name of
897
	 * the modification array. For example, 'header_textcolor', 'header_image',
898
	 * and so on depending on the theme options.
899
	 *
900
	 * @since 3.9.0
901
	 *
902
	 * @param string $value     The new value of the theme mod.
903
	 * @param string $old_value The current value of the theme mod.
904
	 */
905
	$mods[ $name ] = apply_filters( "pre_set_theme_mod_{$name}", $value, $old_value );
906
907
	$theme = get_option( 'stylesheet' );
908
	update_option( "theme_mods_$theme", $mods );
909
}
910
911
/**
912
 * Remove theme modification name from current theme list.
913
 *
914
 * If removing the name also removes all elements, then the entire option will
915
 * be removed.
916
 *
917
 * @since 2.1.0
918
 *
919
 * @param string $name Theme modification name.
920
 */
921
function remove_theme_mod( $name ) {
922
	$mods = get_theme_mods();
923
924
	if ( ! isset( $mods[ $name ] ) )
925
		return;
926
927
	unset( $mods[ $name ] );
928
929
	if ( empty( $mods ) ) {
930
		remove_theme_mods();
931
		return;
932
	}
933
	$theme = get_option( 'stylesheet' );
934
	update_option( "theme_mods_$theme", $mods );
935
}
936
937
/**
938
 * Remove theme modifications option for current theme.
939
 *
940
 * @since 2.1.0
941
 */
942
function remove_theme_mods() {
943
	delete_option( 'theme_mods_' . get_option( 'stylesheet' ) );
944
945
	// Old style.
946
	$theme_name = get_option( 'current_theme' );
947
	if ( false === $theme_name )
948
		$theme_name = wp_get_theme()->get('Name');
949
	delete_option( 'mods_' . $theme_name );
950
}
951
952
/**
953
 * Retrieves the custom header text color in HEX format.
954
 *
955
 * @since 2.1.0
956
 *
957
 * @return string Header text color in HEX format (minus the hash symbol).
958
 */
959
function get_header_textcolor() {
960
	return get_theme_mod('header_textcolor', get_theme_support( 'custom-header', 'default-text-color' ) );
961
}
962
963
/**
964
 * Displays the custom header text color in HEX format (minus the hash symbol).
965
 *
966
 * @since 2.1.0
967
 */
968
function header_textcolor() {
969
	echo get_header_textcolor();
970
}
971
972
/**
973
 * Whether to display the header text.
974
 *
975
 * @since 3.4.0
976
 *
977
 * @return bool
978
 */
979
function display_header_text() {
980
	if ( ! current_theme_supports( 'custom-header', 'header-text' ) )
981
		return false;
982
983
	$text_color = get_theme_mod( 'header_textcolor', get_theme_support( 'custom-header', 'default-text-color' ) );
984
	return 'blank' !== $text_color;
985
}
986
987
/**
988
 * Check whether a header image is set or not.
989
 *
990
 * @since 4.2.0
991
 *
992
 * @see get_header_image()
993
 *
994
 * @return bool Whether a header image is set or not.
995
 */
996
function has_header_image() {
997
	return (bool) get_header_image();
998
}
999
1000
/**
1001
 * Retrieve header image for custom header.
1002
 *
1003
 * @since 2.1.0
1004
 *
1005
 * @return string|false
1006
 */
1007
function get_header_image() {
1008
	$url = get_theme_mod( 'header_image', get_theme_support( 'custom-header', 'default-image' ) );
1009
1010
	if ( 'remove-header' == $url )
1011
		return false;
1012
1013
	if ( is_random_header_image() )
1014
		$url = get_random_header_image();
1015
1016
	return esc_url_raw( set_url_scheme( $url ) );
1017
}
1018
1019
/**
1020
 * Create image tag markup for a custom header image.
1021
 *
1022
 * @since 4.4.0
1023
 *
1024
 * @param array $attr Optional. Additional attributes for the image tag. Can be used
1025
 *                              to override the default attributes. Default empty.
1026
 * @return string HTML image element markup or empty string on failure.
1027
 */
1028
function get_header_image_tag( $attr = array() ) {
1029
	$header = get_custom_header();
1030
1031
	if ( empty( $header->url ) ) {
1032
		return '';
1033
	}
1034
1035
	$width = absint( $header->width );
1036
	$height = absint( $header->height );
1037
1038
	$attr = wp_parse_args(
1039
		$attr,
1040
		array(
1041
			'src' => $header->url,
1042
			'width' => $width,
1043
			'height' => $height,
1044
			'alt' => get_bloginfo( 'name' ),
1045
		)
1046
	);
1047
1048
	// Generate 'srcset' and 'sizes' if not already present.
1049
	if ( empty( $attr['srcset'] ) && ! empty( $header->attachment_id ) ) {
1050
		$image_meta = get_post_meta( $header->attachment_id, '_wp_attachment_metadata', true );
1051
		$size_array = array( $width, $height );
1052
1053
		if ( is_array( $image_meta ) ) {
1054
			$srcset = wp_calculate_image_srcset( $size_array, $header->url, $image_meta, $header->attachment_id );
1055
			$sizes = ! empty( $attr['sizes'] ) ? $attr['sizes'] : wp_calculate_image_sizes( $size_array, $header->url, $image_meta, $header->attachment_id );
1056
1057
			if ( $srcset && $sizes ) {
1058
				$attr['srcset'] = $srcset;
1059
				$attr['sizes'] = $sizes;
1060
			}
1061
		}
1062
	}
1063
1064
	$attr = array_map( 'esc_attr', $attr );
1065
	$html = '<img';
1066
1067
	foreach ( $attr as $name => $value ) {
1068
		$html .= ' ' . $name . '="' . $value . '"';
1069
	}
1070
1071
	$html .= ' />';
1072
1073
	/**
1074
	 * Filters the markup of header images.
1075
	 *
1076
	 * @since 4.4.0
1077
	 *
1078
	 * @param string $html   The HTML image tag markup being filtered.
1079
	 * @param object $header The custom header object returned by 'get_custom_header()'.
1080
	 * @param array  $attr   Array of the attributes for the image tag.
1081
	 */
1082
	return apply_filters( 'get_header_image_tag', $html, $header, $attr );
1083
}
1084
1085
/**
1086
 * Display the image markup for a custom header image.
1087
 *
1088
 * @since 4.4.0
1089
 *
1090
 * @param array $attr Optional. Attributes for the image markup. Default empty.
1091
 */
1092
function the_header_image_tag( $attr = array() ) {
1093
	echo get_header_image_tag( $attr );
1094
}
1095
1096
/**
1097
 * Get random header image data from registered images in theme.
1098
 *
1099
 * @since 3.4.0
1100
 *
1101
 * @access private
1102
 *
1103
 * @global array  $_wp_default_headers
1104
 * @staticvar object $_wp_random_header
1105
 *
1106
 * @return object
1107
 */
1108
function _get_random_header_data() {
1109
	static $_wp_random_header = null;
1110
1111
	if ( empty( $_wp_random_header ) ) {
1112
		global $_wp_default_headers;
1113
		$header_image_mod = get_theme_mod( 'header_image', '' );
1114
		$headers = array();
1115
1116
		if ( 'random-uploaded-image' == $header_image_mod )
1117
			$headers = get_uploaded_header_images();
1118
		elseif ( ! empty( $_wp_default_headers ) ) {
1119
			if ( 'random-default-image' == $header_image_mod ) {
1120
				$headers = $_wp_default_headers;
1121
			} else {
1122
				if ( current_theme_supports( 'custom-header', 'random-default' ) )
1123
					$headers = $_wp_default_headers;
1124
			}
1125
		}
1126
1127
		if ( empty( $headers ) )
1128
			return new stdClass;
1129
1130
		$_wp_random_header = (object) $headers[ array_rand( $headers ) ];
1131
1132
		$_wp_random_header->url =  sprintf( $_wp_random_header->url, get_template_directory_uri(), get_stylesheet_directory_uri() );
1133
		$_wp_random_header->thumbnail_url =  sprintf( $_wp_random_header->thumbnail_url, get_template_directory_uri(), get_stylesheet_directory_uri() );
1134
	}
1135
	return $_wp_random_header;
1136
}
1137
1138
/**
1139
 * Get random header image url from registered images in theme.
1140
 *
1141
 * @since 3.2.0
1142
 *
1143
 * @return string Path to header image
1144
 */
1145
function get_random_header_image() {
1146
	$random_image = _get_random_header_data();
1147
	if ( empty( $random_image->url ) )
1148
		return '';
1149
	return $random_image->url;
1150
}
1151
1152
/**
1153
 * Check if random header image is in use.
1154
 *
1155
 * Always true if user expressly chooses the option in Appearance > Header.
1156
 * Also true if theme has multiple header images registered, no specific header image
1157
 * is chosen, and theme turns on random headers with add_theme_support().
1158
 *
1159
 * @since 3.2.0
1160
 *
1161
 * @param string $type The random pool to use. any|default|uploaded
1162
 * @return bool
1163
 */
1164
function is_random_header_image( $type = 'any' ) {
1165
	$header_image_mod = get_theme_mod( 'header_image', get_theme_support( 'custom-header', 'default-image' ) );
1166
1167
	if ( 'any' == $type ) {
1168
		if ( 'random-default-image' == $header_image_mod || 'random-uploaded-image' == $header_image_mod || ( '' != get_random_header_image() && empty( $header_image_mod ) ) )
1169
			return true;
1170
	} else {
1171
		if ( "random-$type-image" == $header_image_mod )
1172
			return true;
1173
		elseif ( 'default' == $type && empty( $header_image_mod ) && '' != get_random_header_image() )
1174
			return true;
1175
	}
1176
1177
	return false;
1178
}
1179
1180
/**
1181
 * Display header image URL.
1182
 *
1183
 * @since 2.1.0
1184
 */
1185
function header_image() {
1186
	$image = get_header_image();
1187
	if ( $image ) {
1188
		echo esc_url( $image );
1189
	}
1190
}
1191
1192
/**
1193
 * Get the header images uploaded for the current theme.
1194
 *
1195
 * @since 3.2.0
1196
 *
1197
 * @return array
1198
 */
1199
function get_uploaded_header_images() {
1200
	$header_images = array();
1201
1202
	// @todo caching
1203
	$headers = get_posts( array( 'post_type' => 'attachment', 'meta_key' => '_wp_attachment_is_custom_header', 'meta_value' => get_option('stylesheet'), 'orderby' => 'none', 'nopaging' => true ) );
1204
1205
	if ( empty( $headers ) )
1206
		return array();
1207
1208
	foreach ( (array) $headers as $header ) {
1209
		$url = esc_url_raw( wp_get_attachment_url( $header->ID ) );
0 ignored issues
show
It seems like wp_get_attachment_url($header->ID) targeting wp_get_attachment_url() can also be of type false; however, esc_url_raw() does only seem to accept string, did you maybe forget to handle an error condition?
Loading history...
1210
		$header_data = wp_get_attachment_metadata( $header->ID );
1211
		$header_index = $header->ID;
1212
1213
		$header_images[$header_index] = array();
1214
		$header_images[$header_index]['attachment_id'] = $header->ID;
1215
		$header_images[$header_index]['url'] =  $url;
1216
		$header_images[$header_index]['thumbnail_url'] = $url;
1217
		$header_images[$header_index]['alt_text'] = get_post_meta( $header->ID, '_wp_attachment_image_alt', true );
1218
1219
		if ( isset( $header_data['width'] ) )
1220
			$header_images[$header_index]['width'] = $header_data['width'];
1221
		if ( isset( $header_data['height'] ) )
1222
			$header_images[$header_index]['height'] = $header_data['height'];
1223
	}
1224
1225
	return $header_images;
1226
}
1227
1228
/**
1229
 * Get the header image data.
1230
 *
1231
 * @since 3.4.0
1232
 *
1233
 * @global array $_wp_default_headers
1234
 *
1235
 * @return object
1236
 */
1237
function get_custom_header() {
1238
	global $_wp_default_headers;
1239
1240
	if ( is_random_header_image() ) {
1241
		$data = _get_random_header_data();
1242
	} else {
1243
		$data = get_theme_mod( 'header_image_data' );
1244
		if ( ! $data && current_theme_supports( 'custom-header', 'default-image' ) ) {
1245
			$directory_args = array( get_template_directory_uri(), get_stylesheet_directory_uri() );
1246
			$data = array();
1247
			$data['url'] = $data['thumbnail_url'] = vsprintf( get_theme_support( 'custom-header', 'default-image' ), $directory_args );
1248
			if ( ! empty( $_wp_default_headers ) ) {
1249
				foreach ( (array) $_wp_default_headers as $default_header ) {
1250
					$url = vsprintf( $default_header['url'], $directory_args );
1251
					if ( $data['url'] == $url ) {
1252
						$data = $default_header;
1253
						$data['url'] = $url;
1254
						$data['thumbnail_url'] = vsprintf( $data['thumbnail_url'], $directory_args );
1255
						break;
1256
					}
1257
				}
1258
			}
1259
		}
1260
	}
1261
1262
	$default = array(
1263
		'url'           => '',
1264
		'thumbnail_url' => '',
1265
		'width'         => get_theme_support( 'custom-header', 'width' ),
1266
		'height'        => get_theme_support( 'custom-header', 'height' ),
1267
	);
1268
	return (object) wp_parse_args( $data, $default );
1269
}
1270
1271
/**
1272
 * Register a selection of default headers to be displayed by the custom header admin UI.
1273
 *
1274
 * @since 3.0.0
1275
 *
1276
 * @global array $_wp_default_headers
1277
 *
1278
 * @param array $headers Array of headers keyed by a string id. The ids point to arrays containing 'url', 'thumbnail_url', and 'description' keys.
1279
 */
1280
function register_default_headers( $headers ) {
1281
	global $_wp_default_headers;
1282
1283
	$_wp_default_headers = array_merge( (array) $_wp_default_headers, (array) $headers );
1284
}
1285
1286
/**
1287
 * Unregister default headers.
1288
 *
1289
 * This function must be called after register_default_headers() has already added the
1290
 * header you want to remove.
1291
 *
1292
 * @see register_default_headers()
1293
 * @since 3.0.0
1294
 *
1295
 * @global array $_wp_default_headers
1296
 *
1297
 * @param string|array $header The header string id (key of array) to remove, or an array thereof.
1298
 * @return bool|void A single header returns true on success, false on failure.
1299
 *                   There is currently no return value for multiple headers.
1300
 */
1301
function unregister_default_headers( $header ) {
1302
	global $_wp_default_headers;
1303
	if ( is_array( $header ) ) {
1304
		array_map( 'unregister_default_headers', $header );
1305
	} elseif ( isset( $_wp_default_headers[ $header ] ) ) {
1306
		unset( $_wp_default_headers[ $header ] );
1307
		return true;
1308
	} else {
1309
		return false;
1310
	}
1311
}
1312
1313
/**
1314
 * Retrieve background image for custom background.
1315
 *
1316
 * @since 3.0.0
1317
 *
1318
 * @return string
1319
 */
1320
function get_background_image() {
1321
	return get_theme_mod('background_image', get_theme_support( 'custom-background', 'default-image' ) );
1322
}
1323
1324
/**
1325
 * Display background image path.
1326
 *
1327
 * @since 3.0.0
1328
 */
1329
function background_image() {
1330
	echo get_background_image();
1331
}
1332
1333
/**
1334
 * Retrieve value for custom background color.
1335
 *
1336
 * @since 3.0.0
1337
 *
1338
 * @return string
1339
 */
1340
function get_background_color() {
1341
	return get_theme_mod('background_color', get_theme_support( 'custom-background', 'default-color' ) );
1342
}
1343
1344
/**
1345
 * Display background color value.
1346
 *
1347
 * @since 3.0.0
1348
 */
1349
function background_color() {
1350
	echo get_background_color();
1351
}
1352
1353
/**
1354
 * Default custom background callback.
1355
 *
1356
 * @since 3.0.0
1357
 * @access protected
1358
 */
1359
function _custom_background_cb() {
1360
	// $background is the saved custom image, or the default image.
1361
	$background = set_url_scheme( get_background_image() );
1362
1363
	// $color is the saved custom color.
1364
	// A default has to be specified in style.css. It will not be printed here.
1365
	$color = get_background_color();
1366
1367
	if ( $color === get_theme_support( 'custom-background', 'default-color' ) ) {
1368
		$color = false;
1369
	}
1370
1371
	if ( ! $background && ! $color )
0 ignored issues
show
Bug Best Practice introduced by
The expression $color of type false|string is loosely compared to false; this is ambiguous if the string can be empty. You might want to explicitly use === false instead.

In PHP, under loose comparison (like ==, or !=, or switch conditions), values of different types might be equal.

For string values, the empty string '' is a special case, in particular the following results might be unexpected:

''   == false // true
''   == null  // true
'ab' == false // false
'ab' == null  // false

// It is often better to use strict comparison
'' === false // false
'' === null  // false
Loading history...
1372
		return;
1373
1374
	$style = $color ? "background-color: #$color;" : '';
1375
1376
	if ( $background ) {
1377
		$image = " background-image: url('$background');";
1378
1379
		$repeat = get_theme_mod( 'background_repeat', get_theme_support( 'custom-background', 'default-repeat' ) );
1380
		if ( ! in_array( $repeat, array( 'no-repeat', 'repeat-x', 'repeat-y', 'repeat' ) ) )
1381
			$repeat = 'repeat';
1382
		$repeat = " background-repeat: $repeat;";
1383
1384
		$position = get_theme_mod( 'background_position_x', get_theme_support( 'custom-background', 'default-position-x' ) );
1385
		if ( ! in_array( $position, array( 'center', 'right', 'left' ) ) )
1386
			$position = 'left';
1387
		$position = " background-position: top $position;";
1388
1389
		$attachment = get_theme_mod( 'background_attachment', get_theme_support( 'custom-background', 'default-attachment' ) );
1390
		if ( ! in_array( $attachment, array( 'fixed', 'scroll' ) ) )
1391
			$attachment = 'scroll';
1392
		$attachment = " background-attachment: $attachment;";
1393
1394
		$style .= $image . $repeat . $position . $attachment;
1395
	}
1396
?>
1397
<style type="text/css" id="custom-background-css">
1398
body.custom-background { <?php echo trim( $style ); ?> }
1399
</style>
1400
<?php
1401
}
1402
1403
/**
1404
 * Add callback for custom TinyMCE editor stylesheets.
1405
 *
1406
 * The parameter $stylesheet is the name of the stylesheet, relative to
1407
 * the theme root. It also accepts an array of stylesheets.
1408
 * It is optional and defaults to 'editor-style.css'.
1409
 *
1410
 * This function automatically adds another stylesheet with -rtl prefix, e.g. editor-style-rtl.css.
1411
 * If that file doesn't exist, it is removed before adding the stylesheet(s) to TinyMCE.
1412
 * If an array of stylesheets is passed to add_editor_style(),
1413
 * RTL is only added for the first stylesheet.
1414
 *
1415
 * Since version 3.4 the TinyMCE body has .rtl CSS class.
1416
 * It is a better option to use that class and add any RTL styles to the main stylesheet.
1417
 *
1418
 * @since 3.0.0
1419
 *
1420
 * @global array $editor_styles
1421
 *
1422
 * @param array|string $stylesheet Optional. Stylesheet name or array thereof, relative to theme root.
1423
 * 	                               Defaults to 'editor-style.css'
1424
 */
1425
function add_editor_style( $stylesheet = 'editor-style.css' ) {
1426
	add_theme_support( 'editor-style' );
1427
1428
	if ( ! is_admin() )
1429
		return;
1430
1431
	global $editor_styles;
1432
	$editor_styles = (array) $editor_styles;
1433
	$stylesheet    = (array) $stylesheet;
1434
	if ( is_rtl() ) {
1435
		$rtl_stylesheet = str_replace('.css', '-rtl.css', $stylesheet[0]);
1436
		$stylesheet[] = $rtl_stylesheet;
1437
	}
1438
1439
	$editor_styles = array_merge( $editor_styles, $stylesheet );
1440
}
1441
1442
/**
1443
 * Removes all visual editor stylesheets.
1444
 *
1445
 * @since 3.1.0
1446
 *
1447
 * @global array $editor_styles
1448
 *
1449
 * @return bool True on success, false if there were no stylesheets to remove.
1450
 */
1451
function remove_editor_styles() {
1452
	if ( ! current_theme_supports( 'editor-style' ) )
1453
		return false;
1454
	_remove_theme_support( 'editor-style' );
1455
	if ( is_admin() )
1456
		$GLOBALS['editor_styles'] = array();
1457
	return true;
1458
}
1459
1460
/**
1461
 * Retrieve any registered editor stylesheets
1462
 *
1463
 * @since 4.0.0
1464
 *
1465
 * @global array $editor_styles Registered editor stylesheets
1466
 *
1467
 * @return array If registered, a list of editor stylesheet URLs.
1468
 */
1469
function get_editor_stylesheets() {
1470
	$stylesheets = array();
1471
	// load editor_style.css if the current theme supports it
1472
	if ( ! empty( $GLOBALS['editor_styles'] ) && is_array( $GLOBALS['editor_styles'] ) ) {
1473
		$editor_styles = $GLOBALS['editor_styles'];
1474
1475
		$editor_styles = array_unique( array_filter( $editor_styles ) );
1476
		$style_uri = get_stylesheet_directory_uri();
1477
		$style_dir = get_stylesheet_directory();
1478
1479
		// Support externally referenced styles (like, say, fonts).
1480
		foreach ( $editor_styles as $key => $file ) {
1481
			if ( preg_match( '~^(https?:)?//~', $file ) ) {
1482
				$stylesheets[] = esc_url_raw( $file );
1483
				unset( $editor_styles[ $key ] );
1484
			}
1485
		}
1486
1487
		// Look in a parent theme first, that way child theme CSS overrides.
1488
		if ( is_child_theme() ) {
1489
			$template_uri = get_template_directory_uri();
1490
			$template_dir = get_template_directory();
1491
1492
			foreach ( $editor_styles as $key => $file ) {
1493
				if ( $file && file_exists( "$template_dir/$file" ) ) {
1494
					$stylesheets[] = "$template_uri/$file";
1495
				}
1496
			}
1497
		}
1498
1499
		foreach ( $editor_styles as $file ) {
1500
			if ( $file && file_exists( "$style_dir/$file" ) ) {
1501
				$stylesheets[] = "$style_uri/$file";
1502
			}
1503
		}
1504
	}
1505
1506
	/**
1507
	 * Filters the array of stylesheets applied to the editor.
1508
	 *
1509
	 * @since 4.3.0
1510
	 *
1511
	 * @param array $stylesheets Array of stylesheets to be applied to the editor.
1512
	 */
1513
	return apply_filters( 'editor_stylesheets', $stylesheets );
1514
}
1515
1516
/**
1517
 * Registers theme support for a given feature.
1518
 *
1519
 * Must be called in the theme's functions.php file to work.
1520
 * If attached to a hook, it must be {@see 'after_setup_theme'}.
1521
 * The {@see 'init'} hook may be too late for some features.
1522
 *
1523
 * @since 2.9.0
1524
 * @since 3.6.0 The `html5` feature was added
1525
 * @since 3.9.0 The `html5` feature now also accepts 'gallery' and 'caption'
1526
 * @since 4.1.0 The `title-tag` feature was added
1527
 * @since 4.5.0 The `customize-selective-refresh-widgets` feature was added
1528
 *
1529
 * @global array $_wp_theme_features
1530
 *
1531
 * @param string $feature  The feature being added. Likely core values include 'post-formats',
1532
 *                         'post-thumbnails', 'html5', 'custom-logo', 'custom-header-uploads',
1533
 *                         'custom-header', 'custom-background', 'title-tag', etc.
1534
 * @param mixed  $args,... Optional extra arguments to pass along with certain features.
0 ignored issues
show
There is no parameter named $args,.... Was it maybe removed?

This check looks for PHPDoc comments describing methods or function parameters that do not exist on the corresponding method or function.

Consider the following example. The parameter $italy is not defined by the method finale(...).

/**
 * @param array $germany
 * @param array $island
 * @param array $italy
 */
function finale($germany, $island) {
    return "2:1";
}

The most likely cause is that the parameter was removed, but the annotation was not.

Loading history...
1535
 * @return void|bool False on failure, void otherwise.
1536
 */
1537
function add_theme_support( $feature ) {
1538
	global $_wp_theme_features;
1539
1540
	if ( func_num_args() == 1 )
1541
		$args = true;
1542
	else
1543
		$args = array_slice( func_get_args(), 1 );
1544
1545
	switch ( $feature ) {
1546
		case 'post-thumbnails':
1547
			// All post types are already supported.
1548
			if ( true === get_theme_support( 'post-thumbnails' ) ) {
1549
				return;
1550
			}
1551
1552
			/*
1553
			 * Merge post types with any that already declared their support
1554
			 * for post thumbnails.
1555
			 */
1556
			if ( is_array( $args[0] ) && isset( $_wp_theme_features['post-thumbnails'] ) ) {
1557
				$args[0] = array_unique( array_merge( $_wp_theme_features['post-thumbnails'][0], $args[0] ) );
1558
			}
1559
1560
			break;
1561
1562
		case 'post-formats' :
1563
			if ( is_array( $args[0] ) ) {
1564
				$post_formats = get_post_format_slugs();
1565
				unset( $post_formats['standard'] );
1566
1567
				$args[0] = array_intersect( $args[0], array_keys( $post_formats ) );
1568
			}
1569
			break;
1570
1571
		case 'html5' :
1572
			// You can't just pass 'html5', you need to pass an array of types.
1573
			if ( empty( $args[0] ) ) {
1574
				// Build an array of types for back-compat.
1575
				$args = array( 0 => array( 'comment-list', 'comment-form', 'search-form' ) );
1576
			} elseif ( ! is_array( $args[0] ) ) {
1577
				_doing_it_wrong( "add_theme_support( 'html5' )", __( 'You need to pass an array of types.' ), '3.6.1' );
1578
				return false;
1579
			}
1580
1581
			// Calling 'html5' again merges, rather than overwrites.
1582
			if ( isset( $_wp_theme_features['html5'] ) )
1583
				$args[0] = array_merge( $_wp_theme_features['html5'][0], $args[0] );
1584
			break;
1585
1586
		case 'custom-logo':
1587
			if ( ! is_array( $args ) ) {
1588
				$args = array( 0 => array() );
1589
			}
1590
			$defaults = array(
1591
				'width'       => null,
1592
				'height'      => null,
1593
				'flex-width'  => false,
1594
				'flex-height' => false,
1595
				'header-text' => '',
1596
			);
1597
			$args[0] = wp_parse_args( array_intersect_key( $args[0], $defaults ), $defaults );
1598
1599
			// Allow full flexibility if no size is specified.
1600
			if ( is_null( $args[0]['width'] ) && is_null( $args[0]['height'] ) ) {
1601
				$args[0]['flex-width']  = true;
1602
				$args[0]['flex-height'] = true;
1603
			}
1604
			break;
1605
1606
		case 'custom-header-uploads' :
1607
			return add_theme_support( 'custom-header', array( 'uploads' => true ) );
1608
1609
		case 'custom-header' :
1610
			if ( ! is_array( $args ) )
1611
				$args = array( 0 => array() );
1612
1613
			$defaults = array(
1614
				'default-image' => '',
1615
				'random-default' => false,
1616
				'width' => 0,
1617
				'height' => 0,
1618
				'flex-height' => false,
1619
				'flex-width' => false,
1620
				'default-text-color' => '',
1621
				'header-text' => true,
1622
				'uploads' => true,
1623
				'wp-head-callback' => '',
1624
				'admin-head-callback' => '',
1625
				'admin-preview-callback' => '',
1626
			);
1627
1628
			$jit = isset( $args[0]['__jit'] );
1629
			unset( $args[0]['__jit'] );
1630
1631
			// Merge in data from previous add_theme_support() calls.
1632
			// The first value registered wins. (A child theme is set up first.)
1633
			if ( isset( $_wp_theme_features['custom-header'] ) )
1634
				$args[0] = wp_parse_args( $_wp_theme_features['custom-header'][0], $args[0] );
1635
1636
			// Load in the defaults at the end, as we need to insure first one wins.
1637
			// This will cause all constants to be defined, as each arg will then be set to the default.
1638
			if ( $jit )
1639
				$args[0] = wp_parse_args( $args[0], $defaults );
1640
1641
			// If a constant was defined, use that value. Otherwise, define the constant to ensure
1642
			// the constant is always accurate (and is not defined later,  overriding our value).
1643
			// As stated above, the first value wins.
1644
			// Once we get to wp_loaded (just-in-time), define any constants we haven't already.
1645
			// Constants are lame. Don't reference them. This is just for backward compatibility.
1646
1647 View Code Duplication
			if ( defined( 'NO_HEADER_TEXT' ) )
1648
				$args[0]['header-text'] = ! NO_HEADER_TEXT;
1649
			elseif ( isset( $args[0]['header-text'] ) )
1650
				define( 'NO_HEADER_TEXT', empty( $args[0]['header-text'] ) );
1651
1652 View Code Duplication
			if ( defined( 'HEADER_IMAGE_WIDTH' ) )
1653
				$args[0]['width'] = (int) HEADER_IMAGE_WIDTH;
1654
			elseif ( isset( $args[0]['width'] ) )
1655
				define( 'HEADER_IMAGE_WIDTH', (int) $args[0]['width'] );
1656
1657 View Code Duplication
			if ( defined( 'HEADER_IMAGE_HEIGHT' ) )
1658
				$args[0]['height'] = (int) HEADER_IMAGE_HEIGHT;
1659
			elseif ( isset( $args[0]['height'] ) )
1660
				define( 'HEADER_IMAGE_HEIGHT', (int) $args[0]['height'] );
1661
1662 View Code Duplication
			if ( defined( 'HEADER_TEXTCOLOR' ) )
1663
				$args[0]['default-text-color'] = HEADER_TEXTCOLOR;
1664
			elseif ( isset( $args[0]['default-text-color'] ) )
1665
				define( 'HEADER_TEXTCOLOR', $args[0]['default-text-color'] );
1666
1667 View Code Duplication
			if ( defined( 'HEADER_IMAGE' ) )
1668
				$args[0]['default-image'] = HEADER_IMAGE;
1669
			elseif ( isset( $args[0]['default-image'] ) )
1670
				define( 'HEADER_IMAGE', $args[0]['default-image'] );
1671
1672
			if ( $jit && ! empty( $args[0]['default-image'] ) )
1673
				$args[0]['random-default'] = false;
1674
1675
			// If headers are supported, and we still don't have a defined width or height,
1676
			// we have implicit flex sizes.
1677
			if ( $jit ) {
1678
				if ( empty( $args[0]['width'] ) && empty( $args[0]['flex-width'] ) )
1679
					$args[0]['flex-width'] = true;
1680
				if ( empty( $args[0]['height'] ) && empty( $args[0]['flex-height'] ) )
1681
					$args[0]['flex-height'] = true;
1682
			}
1683
1684
			break;
1685
1686
		case 'custom-background' :
1687
			if ( ! is_array( $args ) )
1688
				$args = array( 0 => array() );
1689
1690
			$defaults = array(
1691
				'default-image'          => '',
1692
				'default-repeat'         => 'repeat',
1693
				'default-position-x'     => 'left',
1694
				'default-attachment'     => 'scroll',
1695
				'default-color'          => '',
1696
				'wp-head-callback'       => '_custom_background_cb',
1697
				'admin-head-callback'    => '',
1698
				'admin-preview-callback' => '',
1699
			);
1700
1701
			$jit = isset( $args[0]['__jit'] );
1702
			unset( $args[0]['__jit'] );
1703
1704
			// Merge in data from previous add_theme_support() calls. The first value registered wins.
1705
			if ( isset( $_wp_theme_features['custom-background'] ) )
1706
				$args[0] = wp_parse_args( $_wp_theme_features['custom-background'][0], $args[0] );
1707
1708
			if ( $jit )
1709
				$args[0] = wp_parse_args( $args[0], $defaults );
1710
1711 View Code Duplication
			if ( defined( 'BACKGROUND_COLOR' ) )
1712
				$args[0]['default-color'] = BACKGROUND_COLOR;
1713
			elseif ( isset( $args[0]['default-color'] ) || $jit )
1714
				define( 'BACKGROUND_COLOR', $args[0]['default-color'] );
1715
1716 View Code Duplication
			if ( defined( 'BACKGROUND_IMAGE' ) )
1717
				$args[0]['default-image'] = BACKGROUND_IMAGE;
1718
			elseif ( isset( $args[0]['default-image'] ) || $jit )
1719
				define( 'BACKGROUND_IMAGE', $args[0]['default-image'] );
1720
1721
			break;
1722
1723
		// Ensure that 'title-tag' is accessible in the admin.
1724
		case 'title-tag' :
1725
			// Can be called in functions.php but must happen before wp_loaded, i.e. not in header.php.
1726
			if ( did_action( 'wp_loaded' ) ) {
1727
				/* translators: 1: Theme support 2: hook name */
1728
				_doing_it_wrong( "add_theme_support( 'title-tag' )", sprintf( __( 'Theme support for %1$s should be registered before the %2$s hook.' ),
1729
					'<code>title-tag</code>', '<code>wp_loaded</code>' ), '4.1.0' );
1730
1731
				return false;
1732
			}
1733
	}
1734
1735
	$_wp_theme_features[ $feature ] = $args;
1736
}
1737
1738
/**
1739
 * Registers the internal custom header and background routines.
1740
 *
1741
 * @since 3.4.0
1742
 * @access private
1743
 *
1744
 * @global Custom_Image_Header $custom_image_header
1745
 * @global Custom_Background   $custom_background
1746
 */
1747
function _custom_header_background_just_in_time() {
1748
	global $custom_image_header, $custom_background;
1749
1750
	if ( current_theme_supports( 'custom-header' ) ) {
1751
		// In case any constants were defined after an add_custom_image_header() call, re-run.
1752
		add_theme_support( 'custom-header', array( '__jit' => true ) );
1753
1754
		$args = get_theme_support( 'custom-header' );
1755
		if ( $args[0]['wp-head-callback'] )
1756
			add_action( 'wp_head', $args[0]['wp-head-callback'] );
1757
1758 View Code Duplication
		if ( is_admin() ) {
1759
			require_once( ABSPATH . 'wp-admin/custom-header.php' );
1760
			$custom_image_header = new Custom_Image_Header( $args[0]['admin-head-callback'], $args[0]['admin-preview-callback'] );
1761
		}
1762
	}
1763
1764
	if ( current_theme_supports( 'custom-background' ) ) {
1765
		// In case any constants were defined after an add_custom_background() call, re-run.
1766
		add_theme_support( 'custom-background', array( '__jit' => true ) );
1767
1768
		$args = get_theme_support( 'custom-background' );
1769
		add_action( 'wp_head', $args[0]['wp-head-callback'] );
1770
1771 View Code Duplication
		if ( is_admin() ) {
1772
			require_once( ABSPATH . 'wp-admin/custom-background.php' );
1773
			$custom_background = new Custom_Background( $args[0]['admin-head-callback'], $args[0]['admin-preview-callback'] );
1774
		}
1775
	}
1776
}
1777
1778
/**
1779
 * Adds CSS to hide header text for custom logo, based on Customizer setting.
1780
 *
1781
 * @since 4.5.0
1782
 * @access private
1783
 */
1784
function _custom_logo_header_styles() {
1785
	if ( ! current_theme_supports( 'custom-header', 'header-text' ) && get_theme_support( 'custom-logo', 'header-text' ) && ! get_theme_mod( 'header_text', true ) ) {
1786
		$classes = (array) get_theme_support( 'custom-logo', 'header-text' );
1787
		$classes = array_map( 'sanitize_html_class', $classes );
1788
		$classes = '.' . implode( ', .', $classes );
1789
1790
		?>
1791
		<!-- Custom Logo: hide header text -->
1792
		<style id="custom-logo-css" type="text/css">
1793
			<?php echo $classes; ?> {
1794
				position: absolute;
1795
				clip: rect(1px, 1px, 1px, 1px);
1796
			}
1797
		</style>
1798
	<?php
1799
	}
1800
}
1801
1802
/**
1803
 * Gets the theme support arguments passed when registering that support
1804
 *
1805
 * @since 3.1.0
1806
 *
1807
 * @global array $_wp_theme_features
1808
 *
1809
 * @param string $feature the feature to check
1810
 * @return mixed The array of extra arguments or the value for the registered feature.
1811
 */
1812
function get_theme_support( $feature ) {
1813
	global $_wp_theme_features;
1814
	if ( ! isset( $_wp_theme_features[ $feature ] ) )
1815
		return false;
1816
1817
	if ( func_num_args() <= 1 )
1818
		return $_wp_theme_features[ $feature ];
1819
1820
	$args = array_slice( func_get_args(), 1 );
1821
	switch ( $feature ) {
1822
		case 'custom-logo' :
1823
		case 'custom-header' :
1824 View Code Duplication
		case 'custom-background' :
1825
			if ( isset( $_wp_theme_features[ $feature ][0][ $args[0] ] ) )
1826
				return $_wp_theme_features[ $feature ][0][ $args[0] ];
1827
			return false;
1828
1829
		default :
1830
			return $_wp_theme_features[ $feature ];
1831
	}
1832
}
1833
1834
/**
1835
 * Allows a theme to de-register its support of a certain feature
1836
 *
1837
 * Should be called in the theme's functions.php file. Generally would
1838
 * be used for child themes to override support from the parent theme.
1839
 *
1840
 * @since 3.0.0
1841
 * @see add_theme_support()
1842
 * @param string $feature the feature being added
1843
 * @return bool|void Whether feature was removed.
1844
 */
1845
function remove_theme_support( $feature ) {
1846
	// Blacklist: for internal registrations not used directly by themes.
1847
	if ( in_array( $feature, array( 'editor-style', 'widgets', 'menus' ) ) )
1848
		return false;
1849
1850
	return _remove_theme_support( $feature );
1851
}
1852
1853
/**
1854
 * Do not use. Removes theme support internally, ignorant of the blacklist.
1855
 *
1856
 * @access private
1857
 * @since 3.1.0
1858
 *
1859
 * @global array               $_wp_theme_features
1860
 * @global Custom_Image_Header $custom_image_header
1861
 * @global Custom_Background   $custom_background
1862
 *
1863
 * @param string $feature
1864
 */
1865
function _remove_theme_support( $feature ) {
1866
	global $_wp_theme_features;
1867
1868
	switch ( $feature ) {
1869
		case 'custom-header-uploads' :
1870
			if ( ! isset( $_wp_theme_features['custom-header'] ) )
1871
				return false;
1872
			add_theme_support( 'custom-header', array( 'uploads' => false ) );
1873
			return; // Do not continue - custom-header-uploads no longer exists.
1874
	}
1875
1876
	if ( ! isset( $_wp_theme_features[ $feature ] ) )
1877
		return false;
1878
1879
	switch ( $feature ) {
1880
		case 'custom-header' :
1881
			if ( ! did_action( 'wp_loaded' ) )
1882
				break;
1883
			$support = get_theme_support( 'custom-header' );
1884
			if ( $support[0]['wp-head-callback'] )
1885
				remove_action( 'wp_head', $support[0]['wp-head-callback'] );
1886
			remove_action( 'admin_menu', array( $GLOBALS['custom_image_header'], 'init' ) );
1887
			unset( $GLOBALS['custom_image_header'] );
1888
			break;
1889
1890
		case 'custom-background' :
1891
			if ( ! did_action( 'wp_loaded' ) )
1892
				break;
1893
			$support = get_theme_support( 'custom-background' );
1894
			remove_action( 'wp_head', $support[0]['wp-head-callback'] );
1895
			remove_action( 'admin_menu', array( $GLOBALS['custom_background'], 'init' ) );
1896
			unset( $GLOBALS['custom_background'] );
1897
			break;
1898
	}
1899
1900
	unset( $_wp_theme_features[ $feature ] );
1901
	return true;
1902
}
1903
1904
/**
1905
 * Checks a theme's support for a given feature
1906
 *
1907
 * @since 2.9.0
1908
 *
1909
 * @global array $_wp_theme_features
1910
 *
1911
 * @param string $feature the feature being checked
1912
 * @return bool
1913
 */
1914
function current_theme_supports( $feature ) {
1915
	global $_wp_theme_features;
1916
1917
	if ( 'custom-header-uploads' == $feature )
1918
		return current_theme_supports( 'custom-header', 'uploads' );
1919
1920
	if ( !isset( $_wp_theme_features[$feature] ) )
1921
		return false;
1922
1923
	// If no args passed then no extra checks need be performed
1924
	if ( func_num_args() <= 1 )
1925
		return true;
1926
1927
	$args = array_slice( func_get_args(), 1 );
1928
1929
	switch ( $feature ) {
1930
		case 'post-thumbnails':
1931
			// post-thumbnails can be registered for only certain content/post types by passing
1932
			// an array of types to add_theme_support(). If no array was passed, then
1933
			// any type is accepted
1934
			if ( true === $_wp_theme_features[$feature] )  // Registered for all types
1935
				return true;
1936
			$content_type = $args[0];
1937
			return in_array( $content_type, $_wp_theme_features[$feature][0] );
1938
1939
		case 'html5':
1940
		case 'post-formats':
1941
			// specific post formats can be registered by passing an array of types to
1942
			// add_theme_support()
1943
1944
			// Specific areas of HTML5 support *must* be passed via an array to add_theme_support()
1945
1946
			$type = $args[0];
1947
			return in_array( $type, $_wp_theme_features[$feature][0] );
1948
1949
		case 'custom-logo':
1950
		case 'custom-header':
1951 View Code Duplication
		case 'custom-background':
1952
			// Specific capabilities can be registered by passing an array to add_theme_support().
1953
			return ( isset( $_wp_theme_features[ $feature ][0][ $args[0] ] ) && $_wp_theme_features[ $feature ][0][ $args[0] ] );
1954
	}
1955
1956
	/**
1957
	 * Filters whether the current theme supports a specific feature.
1958
	 *
1959
	 * The dynamic portion of the hook name, `$feature`, refers to the specific theme
1960
	 * feature. Possible values include 'post-formats', 'post-thumbnails', 'custom-background',
1961
	 * 'custom-header', 'menus', 'automatic-feed-links', 'html5', and `customize-selective-refresh-widgets`.
1962
	 *
1963
	 * @since 3.4.0
1964
	 *
1965
	 * @param bool   true     Whether the current theme supports the given feature. Default true.
1966
	 * @param array  $args    Array of arguments for the feature.
1967
	 * @param string $feature The theme feature.
1968
	 */
1969
	return apply_filters( "current_theme_supports-{$feature}", true, $args, $_wp_theme_features[$feature] );
1970
}
1971
1972
/**
1973
 * Checks a theme's support for a given feature before loading the functions which implement it.
1974
 *
1975
 * @since 2.9.0
1976
 *
1977
 * @param string $feature The feature being checked.
1978
 * @param string $include Path to the file.
1979
 * @return bool True if the current theme supports the supplied feature, false otherwise.
1980
 */
1981
function require_if_theme_supports( $feature, $include ) {
1982
	if ( current_theme_supports( $feature ) ) {
1983
		require ( $include );
1984
		return true;
1985
	}
1986
	return false;
1987
}
1988
1989
/**
1990
 * Checks an attachment being deleted to see if it's a header or background image.
1991
 *
1992
 * If true it removes the theme modification which would be pointing at the deleted
1993
 * attachment.
1994
 *
1995
 * @access private
1996
 * @since 3.0.0
1997
 * @since 4.3.0 Also removes `header_image_data`.
1998
 * @since 4.5.0 Also removes custom logo theme mods.
1999
 *
2000
 * @param int $id The attachment id.
2001
 */
2002
function _delete_attachment_theme_mod( $id ) {
2003
	$attachment_image = wp_get_attachment_url( $id );
2004
	$header_image     = get_header_image();
2005
	$background_image = get_background_image();
2006
	$custom_logo_id   = get_theme_mod( 'custom_logo' );
2007
2008
	if ( $custom_logo_id && $custom_logo_id == $id ) {
2009
		remove_theme_mod( 'custom_logo' );
2010
		remove_theme_mod( 'header_text' );
2011
	}
2012
2013
	if ( $header_image && $header_image == $attachment_image ) {
2014
		remove_theme_mod( 'header_image' );
2015
		remove_theme_mod( 'header_image_data' );
2016
	}
2017
2018
	if ( $background_image && $background_image == $attachment_image ) {
2019
		remove_theme_mod( 'background_image' );
2020
	}
2021
}
2022
2023
/**
2024
 * Checks if a theme has been changed and runs 'after_switch_theme' hook on the next WP load.
2025
 *
2026
 * See {@see 'after_switch_theme'}.
2027
 *
2028
 * @since 3.3.0
2029
 */
2030
function check_theme_switched() {
2031
	if ( $stylesheet = get_option( 'theme_switched' ) ) {
2032
		$old_theme = wp_get_theme( $stylesheet );
2033
2034
		// Prevent retrieve_widgets() from running since Customizer already called it up front
2035
		if ( get_option( 'theme_switched_via_customizer' ) ) {
2036
			remove_action( 'after_switch_theme', '_wp_sidebars_changed' );
2037
			update_option( 'theme_switched_via_customizer', false );
2038
		}
2039
2040
		if ( $old_theme->exists() ) {
2041
			/**
2042
			 * Fires on the first WP load after a theme switch if the old theme still exists.
2043
			 *
2044
			 * This action fires multiple times and the parameters differs
2045
			 * according to the context, if the old theme exists or not.
2046
			 * If the old theme is missing, the parameter will be the slug
2047
			 * of the old theme.
2048
			 *
2049
			 * @since 3.3.0
2050
			 *
2051
			 * @param string   $old_name  Old theme name.
2052
			 * @param WP_Theme $old_theme WP_Theme instance of the old theme.
2053
			 */
2054
			do_action( 'after_switch_theme', $old_theme->get( 'Name' ), $old_theme );
2055
		} else {
2056
			/** This action is documented in wp-includes/theme.php */
2057
			do_action( 'after_switch_theme', $stylesheet );
2058
		}
2059
		flush_rewrite_rules();
2060
2061
		update_option( 'theme_switched', false );
2062
	}
2063
}
2064
2065
/**
2066
 * Includes and instantiates the WP_Customize_Manager class.
2067
 *
2068
 * Loads the Customizer at plugins_loaded when accessing the customize.php admin
2069
 * page or when any request includes a wp_customize=on param, either as a GET
2070
 * query var or as POST data. This param is a signal for whether to bootstrap
2071
 * the Customizer when WordPress is loading, especially in the Customizer preview
2072
 * or when making Customizer Ajax requests for widgets or menus.
2073
 *
2074
 * @since 3.4.0
2075
 *
2076
 * @global WP_Customize_Manager $wp_customize
2077
 */
2078
function _wp_customize_include() {
2079
	if ( ! ( ( isset( $_REQUEST['wp_customize'] ) && 'on' == $_REQUEST['wp_customize'] )
2080
		|| ( is_admin() && 'customize.php' == basename( $_SERVER['PHP_SELF'] ) )
2081
	) ) {
2082
		return;
2083
	}
2084
2085
	require_once ABSPATH . WPINC . '/class-wp-customize-manager.php';
2086
	$GLOBALS['wp_customize'] = new WP_Customize_Manager();
2087
}
2088
2089
/**
2090
 * Adds settings for the customize-loader script.
2091
 *
2092
 * @since 3.4.0
2093
 */
2094
function _wp_customize_loader_settings() {
2095
	$admin_origin = parse_url( admin_url() );
2096
	$home_origin  = parse_url( home_url() );
2097
	$cross_domain = ( strtolower( $admin_origin[ 'host' ] ) != strtolower( $home_origin[ 'host' ] ) );
2098
2099
	$browser = array(
2100
		'mobile' => wp_is_mobile(),
2101
		'ios'    => wp_is_mobile() && preg_match( '/iPad|iPod|iPhone/', $_SERVER['HTTP_USER_AGENT'] ),
2102
	);
2103
2104
	$settings = array(
2105
		'url'           => esc_url( admin_url( 'customize.php' ) ),
2106
		'isCrossDomain' => $cross_domain,
2107
		'browser'       => $browser,
2108
		'l10n'          => array(
2109
			'saveAlert'       => __( 'The changes you made will be lost if you navigate away from this page.' ),
2110
			'mainIframeTitle' => __( 'Customizer' ),
2111
		),
2112
	);
2113
2114
	$script = 'var _wpCustomizeLoaderSettings = ' . wp_json_encode( $settings ) . ';';
2115
2116
	$wp_scripts = wp_scripts();
2117
	$data = $wp_scripts->get_data( 'customize-loader', 'data' );
2118
	if ( $data )
2119
		$script = "$data\n$script";
2120
2121
	$wp_scripts->add_data( 'customize-loader', 'data', $script );
2122
}
2123
2124
/**
2125
 * Returns a URL to load the Customizer.
2126
 *
2127
 * @since 3.4.0
2128
 *
2129
 * @param string $stylesheet Optional. Theme to customize. Defaults to current theme.
2130
 * 	                         The theme's stylesheet will be urlencoded if necessary.
2131
 * @return string
2132
 */
2133
function wp_customize_url( $stylesheet = null ) {
2134
	$url = admin_url( 'customize.php' );
2135
	if ( $stylesheet )
0 ignored issues
show
Bug Best Practice introduced by
The expression $stylesheet of type string|null is loosely compared to true; this is ambiguous if the string can be empty. You might want to explicitly use !== null instead.

In PHP, under loose comparison (like ==, or !=, or switch conditions), values of different types might be equal.

For string values, the empty string '' is a special case, in particular the following results might be unexpected:

''   == false // true
''   == null  // true
'ab' == false // false
'ab' == null  // false

// It is often better to use strict comparison
'' === false // false
'' === null  // false
Loading history...
2136
		$url .= '?theme=' . urlencode( $stylesheet );
2137
	return esc_url( $url );
2138
}
2139
2140
/**
2141
 * Prints a script to check whether or not the Customizer is supported,
2142
 * and apply either the no-customize-support or customize-support class
2143
 * to the body.
2144
 *
2145
 * This function MUST be called inside the body tag.
2146
 *
2147
 * Ideally, call this function immediately after the body tag is opened.
2148
 * This prevents a flash of unstyled content.
2149
 *
2150
 * It is also recommended that you add the "no-customize-support" class
2151
 * to the body tag by default.
2152
 *
2153
 * @since 3.4.0
2154
 */
2155
function wp_customize_support_script() {
2156
	$admin_origin = parse_url( admin_url() );
2157
	$home_origin  = parse_url( home_url() );
2158
	$cross_domain = ( strtolower( $admin_origin[ 'host' ] ) != strtolower( $home_origin[ 'host' ] ) );
2159
2160
	?>
2161
	<script type="text/javascript">
2162
		(function() {
2163
			var request, b = document.body, c = 'className', cs = 'customize-support', rcs = new RegExp('(^|\\s+)(no-)?'+cs+'(\\s+|$)');
2164
2165
<?php		if ( $cross_domain ): ?>
2166
			request = (function(){ var xhr = new XMLHttpRequest(); return ('withCredentials' in xhr); })();
2167
<?php		else: ?>
2168
			request = true;
2169
<?php		endif; ?>
2170
2171
			b[c] = b[c].replace( rcs, ' ' );
2172
			b[c] += ( window.postMessage && request ? ' ' : ' no-' ) + cs;
2173
		}());
2174
	</script>
2175
	<?php
2176
}
2177
2178
/**
2179
 * Whether the site is being previewed in the Customizer.
2180
 *
2181
 * @since 4.0.0
2182
 *
2183
 * @global WP_Customize_Manager $wp_customize Customizer instance.
2184
 *
2185
 * @return bool True if the site is being previewed in the Customizer, false otherwise.
2186
 */
2187
function is_customize_preview() {
2188
	global $wp_customize;
2189
2190
	return ( $wp_customize instanceof WP_Customize_Manager ) && $wp_customize->is_preview();
2191
}
2192