~canonical-sysadmins/wordpress/4.4.1

1 by Jacek Nykis
Initial commit
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
 *
275
 * @return string Template name.
276
 */
277
function get_template() {
278
	/**
279
	 * Filter the name of the current theme.
280
	 *
281
	 * @since 1.5.0
282
	 *
283
	 * @param string $template Current theme's directory name.
284
	 */
285
	return apply_filters( 'template', get_option( 'template' ) );
286
}
287
288
/**
289
 * Retrieve current theme directory.
290
 *
291
 * @since 1.5.0
292
 *
293
 * @return string Template directory path.
294
 */
295
function get_template_directory() {
296
	$template = get_template();
297
	$theme_root = get_theme_root( $template );
298
	$template_dir = "$theme_root/$template";
299
300
	/**
301
	 * Filter the current theme directory path.
302
	 *
303
	 * @since 1.5.0
304
	 *
305
	 * @param string $template_dir The URI of the current theme directory.
306
	 * @param string $template     Directory name of the current theme.
307
	 * @param string $theme_root   Absolute path to the themes directory.
308
	 */
309
	return apply_filters( 'template_directory', $template_dir, $template, $theme_root );
310
}
311
312
/**
313
 * Retrieve theme directory URI.
314
 *
315
 * @since 1.5.0
316
 *
317
 * @return string Template directory URI.
318
 */
319
function get_template_directory_uri() {
320
	$template = str_replace( '%2F', '/', rawurlencode( get_template() ) );
321
	$theme_root_uri = get_theme_root_uri( $template );
322
	$template_dir_uri = "$theme_root_uri/$template";
323
324
	/**
325
	 * Filter the current theme directory URI.
326
	 *
327
	 * @since 1.5.0
328
	 *
329
	 * @param string $template_dir_uri The URI of the current theme directory.
330
	 * @param string $template         Directory name of the current theme.
331
	 * @param string $theme_root_uri   The themes root URI.
332
	 */
333
	return apply_filters( 'template_directory_uri', $template_dir_uri, $template, $theme_root_uri );
334
}
335
336
/**
337
 * Retrieve theme roots.
338
 *
339
 * @since 2.9.0
340
 *
341
 * @return array|string An array of theme roots keyed by template/stylesheet or a single theme root if all themes have the same root.
342
 */
343
function get_theme_roots() {
344
	global $wp_theme_directories;
345
346
	if ( count($wp_theme_directories) <= 1 )
347
		return '/themes';
348
349
	$theme_roots = get_site_transient( 'theme_roots' );
350
	if ( false === $theme_roots ) {
351
		search_theme_directories( true ); // Regenerate the transient.
352
		$theme_roots = get_site_transient( 'theme_roots' );
353
	}
354
	return $theme_roots;
355
}
356
357
/**
358
 * Register a directory that contains themes.
359
 *
360
 * @since 2.9.0
361
 *
362
 * @param string $directory Either the full filesystem path to a theme folder or a folder within WP_CONTENT_DIR
363
 * @return bool
364
 */
365
function register_theme_directory( $directory ) {
366
	global $wp_theme_directories;
367
368
	if ( ! file_exists( $directory ) ) {
369
		// Try prepending as the theme directory could be relative to the content directory
370
		$directory = WP_CONTENT_DIR . '/' . $directory;
371
		// If this directory does not exist, return and do not register
372
		if ( ! file_exists( $directory ) ) {
373
			return false;
374
		}
375
	}
376
377
	if ( ! is_array( $wp_theme_directories ) ) {
378
		$wp_theme_directories = array();
379
	}
380
381
	$untrailed = untrailingslashit( $directory );
382
	if ( ! empty( $untrailed ) && ! in_array( $untrailed, $wp_theme_directories ) ) {
383
		$wp_theme_directories[] = $untrailed;
384
	}
385
386
	return true;
387
}
388
389
/**
390
 * Search all registered theme directories for complete and valid themes.
391
 *
392
 * @since 2.9.0
393
 *
394
 * @param bool $force Optional. Whether to force a new directory scan. Defaults to false.
395
 * @return array Valid themes found
396
 */
397
function search_theme_directories( $force = false ) {
398
	global $wp_theme_directories;
399
	if ( empty( $wp_theme_directories ) )
400
		return false;
401
402
	static $found_themes;
403
	if ( ! $force && isset( $found_themes ) )
404
		return $found_themes;
405
406
	$found_themes = array();
407
408
	$wp_theme_directories = (array) $wp_theme_directories;
1.1.4 by Paul Gear
new upstream release 4.2
409
	$relative_theme_roots = array();
1 by Jacek Nykis
Initial commit
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
678
/**
679
 * Private function to modify the current template when previewing a theme
680
 *
681
 * @since 2.9.0
682
 * @access private
683
 *
684
 * @return string
685
 */
686
function _preview_theme_template_filter() {
687
	return isset($_GET['template']) ? $_GET['template'] : '';
688
}
689
690
/**
691
 * Private function to modify the current stylesheet when previewing a theme
692
 *
693
 * @since 2.9.0
694
 * @access private
695
 *
696
 * @return string
697
 */
698
function _preview_theme_stylesheet_filter() {
699
	return isset($_GET['stylesheet']) ? $_GET['stylesheet'] : '';
700
}
701
702
/**
703
 * Callback function for ob_start() to capture all links in the theme.
704
 *
705
 * @since 2.6.0
706
 * @access private
707
 *
708
 * @param string $content
709
 * @return string
710
 */
711
function preview_theme_ob_filter( $content ) {
9 by Dave Lawson
Merge 4.2.4 from upstream
712
	return $content;
1 by Jacek Nykis
Initial commit
713
}
714
715
/**
716
 * Manipulates preview theme links in order to control and maintain location.
717
 *
718
 * Callback function for preg_replace_callback() to accept and filter matches.
719
 *
720
 * @since 2.6.0
721
 * @access private
722
 *
723
 * @param array $matches
724
 * @return string
725
 */
726
function preview_theme_ob_filter_callback( $matches ) {
9 by Dave Lawson
Merge 4.2.4 from upstream
727
	return $matches[0];
1 by Jacek Nykis
Initial commit
728
}
729
730
/**
731
 * Switches the theme.
732
 *
733
 * Accepts one argument: $stylesheet of the theme. It also accepts an additional function signature
734
 * of two arguments: $template then $stylesheet. This is for backwards compatibility.
735
 *
736
 * @since 2.5.0
737
 *
738
 * @param string $stylesheet Stylesheet name
739
 */
740
function switch_theme( $stylesheet ) {
741
	global $wp_theme_directories, $wp_customize, $sidebars_widgets;
742
743
	$_sidebars_widgets = null;
744
	if ( 'wp_ajax_customize_save' === current_action() ) {
745
		$_sidebars_widgets = $wp_customize->post_value( $wp_customize->get_setting( 'old_sidebars_widgets_data' ) );
746
	} elseif ( is_array( $sidebars_widgets ) ) {
747
		$_sidebars_widgets = $sidebars_widgets;
748
	}
749
750
	if ( is_array( $_sidebars_widgets ) ) {
751
		set_theme_mod( 'sidebars_widgets', array( 'time' => time(), 'data' => $_sidebars_widgets ) );
752
	}
753
754
	$old_theme  = wp_get_theme();
755
	$new_theme = wp_get_theme( $stylesheet );
756
757
	if ( func_num_args() > 1 ) {
758
		$template = $stylesheet;
759
		$stylesheet = func_get_arg( 1 );
760
	} else {
761
		$template = $new_theme->get_template();
762
	}
763
764
	update_option( 'template', $template );
765
	update_option( 'stylesheet', $stylesheet );
766
767
	if ( count( $wp_theme_directories ) > 1 ) {
768
		update_option( 'template_root', get_raw_theme_root( $template, true ) );
769
		update_option( 'stylesheet_root', get_raw_theme_root( $stylesheet, true ) );
770
	} else {
771
		delete_option( 'template_root' );
772
		delete_option( 'stylesheet_root' );
773
	}
774
775
	$new_name  = $new_theme->get('Name');
776
777
	update_option( 'current_theme', $new_name );
778
779
	// Migrate from the old mods_{name} option to theme_mods_{slug}.
780
	if ( is_admin() && false === get_option( 'theme_mods_' . $stylesheet ) ) {
781
		$default_theme_mods = (array) get_option( 'mods_' . $new_name );
782
		add_option( "theme_mods_$stylesheet", $default_theme_mods );
783
	} else {
784
		/*
1.1.1 by Nick Moffitt
New Upstream Version 4.1
785
		 * Since retrieve_widgets() is called when initializing a theme in the Customizer,
1 by Jacek Nykis
Initial commit
786
		 * we need to to remove the theme mods to avoid overwriting changes made via
1.1.1 by Nick Moffitt
New Upstream Version 4.1
787
		 * the Customizer when accessing wp-admin/widgets.php.
1 by Jacek Nykis
Initial commit
788
		 */
789
		if ( 'wp_ajax_customize_save' === current_action() ) {
790
			remove_theme_mod( 'sidebars_widgets' );
791
		}
792
	}
793
794
	update_option( 'theme_switched', $old_theme->get_stylesheet() );
795
	/**
796
	 * Fires after the theme is switched.
797
	 *
798
	 * @since 1.5.0
799
	 *
800
	 * @param string   $new_name  Name of the new theme.
801
	 * @param WP_Theme $new_theme WP_Theme instance of the new theme.
802
	 */
803
	do_action( 'switch_theme', $new_name, $new_theme );
804
}
805
806
/**
807
 * Checks that current theme files 'index.php' and 'style.css' exists.
808
 *
809
 * Does not check the default theme, which is the fallback and should always exist.
810
 * Will switch theme to the fallback theme if current theme does not validate.
811
 * You can use the 'validate_current_theme' filter to return false to
812
 * disable this functionality.
813
 *
814
 * @since 1.5.0
815
 * @see WP_DEFAULT_THEME
816
 *
817
 * @return bool
818
 */
819
function validate_current_theme() {
820
	/**
821
	 * Filter whether to validate the current theme.
822
	 *
823
	 * @since 2.7.0
824
	 *
825
	 * @param bool true Validation flag to check the current theme.
826
	 */
827
	if ( defined('WP_INSTALLING') || ! apply_filters( 'validate_current_theme', true ) )
828
		return true;
829
830
	if ( get_template() != WP_DEFAULT_THEME && !file_exists(get_template_directory() . '/index.php') ) {
831
		switch_theme( WP_DEFAULT_THEME );
832
		return false;
833
	}
834
835
	if ( get_stylesheet() != WP_DEFAULT_THEME && !file_exists(get_template_directory() . '/style.css') ) {
836
		switch_theme( WP_DEFAULT_THEME );
837
		return false;
838
	}
839
840
	if ( is_child_theme() && ! file_exists( get_stylesheet_directory() . '/style.css' ) ) {
841
		switch_theme( WP_DEFAULT_THEME );
842
		return false;
843
	}
844
845
	return true;
846
}
847
848
/**
849
 * Retrieve all theme modifications.
850
 *
851
 * @since 3.1.0
852
 *
1.1.1 by Nick Moffitt
New Upstream Version 4.1
853
 * @return array|null Theme modifications.
1 by Jacek Nykis
Initial commit
854
 */
855
function get_theme_mods() {
856
	$theme_slug = get_option( 'stylesheet' );
857
	if ( false === ( $mods = get_option( "theme_mods_$theme_slug" ) ) ) {
858
		$theme_name = get_option( 'current_theme' );
859
		if ( false === $theme_name )
860
			$theme_name = wp_get_theme()->get('Name');
861
		$mods = get_option( "mods_$theme_name" ); // Deprecated location.
862
		if ( is_admin() && false !== $mods ) {
863
			update_option( "theme_mods_$theme_slug", $mods );
864
			delete_option( "mods_$theme_name" );
865
		}
866
	}
867
	return $mods;
868
}
869
870
/**
871
 * Retrieve theme modification value for the current theme.
872
 *
873
 * If the modification name does not exist, then the $default will be passed
874
 * through {@link http://php.net/sprintf sprintf()} PHP function with the first
875
 * string the template directory URI and the second string the stylesheet
876
 * directory URI.
877
 *
878
 * @since 2.1.0
879
 *
880
 * @param string $name Theme modification name.
881
 * @param bool|string $default
882
 * @return string
883
 */
884
function get_theme_mod( $name, $default = false ) {
885
	$mods = get_theme_mods();
886
887
	if ( isset( $mods[$name] ) ) {
888
		/**
889
		 * Filter the theme modification, or 'theme_mod', value.
890
		 *
1.1.1 by Nick Moffitt
New Upstream Version 4.1
891
		 * The dynamic portion of the hook name, `$name`, refers to
1 by Jacek Nykis
Initial commit
892
		 * the key name of the modification array. For example,
893
		 * 'header_textcolor', 'header_image', and so on depending
894
		 * on the theme options.
895
		 *
896
		 * @since 2.2.0
897
		 *
898
		 * @param string $current_mod The value of the current theme modification.
899
		 */
900
		return apply_filters( "theme_mod_{$name}", $mods[$name] );
901
	}
902
903
	if ( is_string( $default ) )
904
		$default = sprintf( $default, get_template_directory_uri(), get_stylesheet_directory_uri() );
905
906
	/** This filter is documented in wp-includes/theme.php */
907
	return apply_filters( "theme_mod_{$name}", $default );
908
}
909
910
/**
911
 * Update theme modification value for the current theme.
912
 *
913
 * @since 2.1.0
914
 *
915
 * @param string $name Theme modification name.
1.1.4 by Paul Gear
new upstream release 4.2
916
 * @param mixed  $value theme modification value.
1 by Jacek Nykis
Initial commit
917
 */
918
function set_theme_mod( $name, $value ) {
919
	$mods = get_theme_mods();
920
	$old_value = isset( $mods[ $name ] ) ? $mods[ $name ] : false;
921
922
	/**
923
	 * Filter the theme mod value on save.
924
	 *
1.1.1 by Nick Moffitt
New Upstream Version 4.1
925
	 * The dynamic portion of the hook name, `$name`, refers to the key name of
1 by Jacek Nykis
Initial commit
926
	 * the modification array. For example, 'header_textcolor', 'header_image',
927
	 * and so on depending on the theme options.
928
	 *
929
	 * @since 3.9.0
930
	 *
931
	 * @param string $value     The new value of the theme mod.
932
	 * @param string $old_value The current value of the theme mod.
933
	 */
934
	$mods[ $name ] = apply_filters( "pre_set_theme_mod_$name", $value, $old_value );
935
936
	$theme = get_option( 'stylesheet' );
937
	update_option( "theme_mods_$theme", $mods );
938
}
939
940
/**
941
 * Remove theme modification name from current theme list.
942
 *
943
 * If removing the name also removes all elements, then the entire option will
944
 * be removed.
945
 *
946
 * @since 2.1.0
947
 *
948
 * @param string $name Theme modification name.
949
 * @return null
950
 */
951
function remove_theme_mod( $name ) {
952
	$mods = get_theme_mods();
953
954
	if ( ! isset( $mods[ $name ] ) )
955
		return;
956
957
	unset( $mods[ $name ] );
958
959
	if ( empty( $mods ) )
960
		return remove_theme_mods();
961
962
	$theme = get_option( 'stylesheet' );
963
	update_option( "theme_mods_$theme", $mods );
964
}
965
966
/**
967
 * Remove theme modifications option for current theme.
968
 *
969
 * @since 2.1.0
970
 */
971
function remove_theme_mods() {
972
	delete_option( 'theme_mods_' . get_option( 'stylesheet' ) );
973
974
	// Old style.
975
	$theme_name = get_option( 'current_theme' );
976
	if ( false === $theme_name )
977
		$theme_name = wp_get_theme()->get('Name');
978
	delete_option( 'mods_' . $theme_name );
979
}
980
981
/**
982
 * Retrieve text color for custom header.
983
 *
984
 * @since 2.1.0
985
 *
986
 * @return string
987
 */
988
function get_header_textcolor() {
989
	return get_theme_mod('header_textcolor', get_theme_support( 'custom-header', 'default-text-color' ) );
990
}
991
992
/**
993
 * Display text color for custom header.
994
 *
995
 * @since 2.1.0
996
 */
997
function header_textcolor() {
998
	echo get_header_textcolor();
999
}
1000
1001
/**
1002
 * Whether to display the header text.
1003
 *
1004
 * @since 3.4.0
1005
 *
1006
 * @return bool
1007
 */
1008
function display_header_text() {
1009
	if ( ! current_theme_supports( 'custom-header', 'header-text' ) )
1010
		return false;
1011
1012
	$text_color = get_theme_mod( 'header_textcolor', get_theme_support( 'custom-header', 'default-text-color' ) );
1013
	return 'blank' != $text_color;
1014
}
1015
1016
/**
1.1.4 by Paul Gear
new upstream release 4.2
1017
 * Check whether a header image is set or not.
1018
 *
1019
 * @since 4.2.0
1020
 *
1021
 * @see get_header_image()
1022
 *
1023
 * @return bool Whether a header image is set or not.
1024
 */
1025
function has_header_image() {
1026
	return (bool) get_header_image();
1027
}
1028
1029
/**
1 by Jacek Nykis
Initial commit
1030
 * Retrieve header image for custom header.
1031
 *
1032
 * @since 2.1.0
1033
 *
1.1.4 by Paul Gear
new upstream release 4.2
1034
 * @return string|false
1 by Jacek Nykis
Initial commit
1035
 */
1036
function get_header_image() {
1037
	$url = get_theme_mod( 'header_image', get_theme_support( 'custom-header', 'default-image' ) );
1038
1039
	if ( 'remove-header' == $url )
1040
		return false;
1041
1042
	if ( is_random_header_image() )
1043
		$url = get_random_header_image();
1044
1045
	return esc_url_raw( set_url_scheme( $url ) );
1046
}
1047
1048
/**
1049
 * Get random header image data from registered images in theme.
1050
 *
1051
 * @since 3.4.0
1052
 *
1053
 * @access private
1054
 *
1055
 * @return string Path to header image
1056
 */
1057
1058
function _get_random_header_data() {
1059
	static $_wp_random_header;
1060
1061
	if ( empty( $_wp_random_header ) ) {
1062
		global $_wp_default_headers;
1063
		$header_image_mod = get_theme_mod( 'header_image', '' );
1064
		$headers = array();
1065
1066
		if ( 'random-uploaded-image' == $header_image_mod )
1067
			$headers = get_uploaded_header_images();
1068
		elseif ( ! empty( $_wp_default_headers ) ) {
1069
			if ( 'random-default-image' == $header_image_mod ) {
1070
				$headers = $_wp_default_headers;
1071
			} else {
1072
				if ( current_theme_supports( 'custom-header', 'random-default' ) )
1073
					$headers = $_wp_default_headers;
1074
			}
1075
		}
1076
1077
		if ( empty( $headers ) )
1078
			return new stdClass;
1079
1080
		$_wp_random_header = (object) $headers[ array_rand( $headers ) ];
1081
1082
		$_wp_random_header->url =  sprintf( $_wp_random_header->url, get_template_directory_uri(), get_stylesheet_directory_uri() );
1083
		$_wp_random_header->thumbnail_url =  sprintf( $_wp_random_header->thumbnail_url, get_template_directory_uri(), get_stylesheet_directory_uri() );
1084
	}
1085
	return $_wp_random_header;
1086
}
1087
1088
/**
1089
 * Get random header image url from registered images in theme.
1090
 *
1091
 * @since 3.2.0
1092
 *
1093
 * @return string Path to header image
1094
 */
1095
1096
function get_random_header_image() {
1097
	$random_image = _get_random_header_data();
1098
	if ( empty( $random_image->url ) )
1099
		return '';
1100
	return $random_image->url;
1101
}
1102
1103
/**
1104
 * Check if random header image is in use.
1105
 *
1106
 * Always true if user expressly chooses the option in Appearance > Header.
1107
 * Also true if theme has multiple header images registered, no specific header image
1108
 * is chosen, and theme turns on random headers with add_theme_support().
1109
 *
1110
 * @since 3.2.0
1111
 *
1112
 * @param string $type The random pool to use. any|default|uploaded
1113
 * @return boolean
1114
 */
1115
function is_random_header_image( $type = 'any' ) {
1116
	$header_image_mod = get_theme_mod( 'header_image', get_theme_support( 'custom-header', 'default-image' ) );
1117
1118
	if ( 'any' == $type ) {
1119
		if ( 'random-default-image' == $header_image_mod || 'random-uploaded-image' == $header_image_mod || ( '' != get_random_header_image() && empty( $header_image_mod ) ) )
1120
			return true;
1121
	} else {
1122
		if ( "random-$type-image" == $header_image_mod )
1123
			return true;
1124
		elseif ( 'default' == $type && empty( $header_image_mod ) && '' != get_random_header_image() )
1125
			return true;
1126
	}
1127
1128
	return false;
1129
}
1130
1131
/**
1132
 * Display header image URL.
1133
 *
1134
 * @since 2.1.0
1135
 */
1136
function header_image() {
1.1.4 by Paul Gear
new upstream release 4.2
1137
	$image = get_header_image();
1138
	if ( $image ) {
1139
		echo esc_url( $image );
1140
	}
1 by Jacek Nykis
Initial commit
1141
}
1142
1143
/**
1144
 * Get the header images uploaded for the current theme.
1145
 *
1146
 * @since 3.2.0
1147
 *
1148
 * @return array
1149
 */
1150
function get_uploaded_header_images() {
1151
	$header_images = array();
1152
1153
	// @todo caching
1154
	$headers = get_posts( array( 'post_type' => 'attachment', 'meta_key' => '_wp_attachment_is_custom_header', 'meta_value' => get_option('stylesheet'), 'orderby' => 'none', 'nopaging' => true ) );
1155
1156
	if ( empty( $headers ) )
1157
		return array();
1158
1159
	foreach ( (array) $headers as $header ) {
1160
		$url = esc_url_raw( wp_get_attachment_url( $header->ID ) );
1161
		$header_data = wp_get_attachment_metadata( $header->ID );
1162
		$header_index = basename($url);
1163
		$header_images[$header_index] = array();
1164
		$header_images[$header_index]['attachment_id'] =  $header->ID;
1165
		$header_images[$header_index]['url'] =  $url;
1166
		$header_images[$header_index]['thumbnail_url'] =  $url;
1167
		if ( isset( $header_data['width'] ) )
1168
			$header_images[$header_index]['width'] = $header_data['width'];
1169
		if ( isset( $header_data['height'] ) )
1170
			$header_images[$header_index]['height'] = $header_data['height'];
1171
	}
1172
1173
	return $header_images;
1174
}
1175
1176
/**
1177
 * Get the header image data.
1178
 *
1179
 * @since 3.4.0
1180
 *
1181
 * @return object
1182
 */
1183
function get_custom_header() {
1184
	global $_wp_default_headers;
1185
1186
	if ( is_random_header_image() ) {
1187
		$data = _get_random_header_data();
1188
	} else {
1189
		$data = get_theme_mod( 'header_image_data' );
1190
		if ( ! $data && current_theme_supports( 'custom-header', 'default-image' ) ) {
1191
			$directory_args = array( get_template_directory_uri(), get_stylesheet_directory_uri() );
1192
			$data = array();
1193
			$data['url'] = $data['thumbnail_url'] = vsprintf( get_theme_support( 'custom-header', 'default-image' ), $directory_args );
1194
			if ( ! empty( $_wp_default_headers ) ) {
1195
				foreach ( (array) $_wp_default_headers as $default_header ) {
1196
					$url = vsprintf( $default_header['url'], $directory_args );
1197
					if ( $data['url'] == $url ) {
1198
						$data = $default_header;
1199
						$data['url'] = $url;
1200
						$data['thumbnail_url'] = vsprintf( $data['thumbnail_url'], $directory_args );
1201
						break;
1202
					}
1203
				}
1204
			}
1205
		}
1206
	}
1207
1208
	$default = array(
1209
		'url'           => '',
1210
		'thumbnail_url' => '',
1211
		'width'         => get_theme_support( 'custom-header', 'width' ),
1212
		'height'        => get_theme_support( 'custom-header', 'height' ),
1213
	);
1214
	return (object) wp_parse_args( $data, $default );
1215
}
1216
1217
/**
1218
 * Register a selection of default headers to be displayed by the custom header admin UI.
1219
 *
1220
 * @since 3.0.0
1221
 *
1222
 * @param array $headers Array of headers keyed by a string id. The ids point to arrays containing 'url', 'thumbnail_url', and 'description' keys.
1223
 */
1224
function register_default_headers( $headers ) {
1225
	global $_wp_default_headers;
1226
1227
	$_wp_default_headers = array_merge( (array) $_wp_default_headers, (array) $headers );
1228
}
1229
1230
/**
1231
 * Unregister default headers.
1232
 *
1233
 * This function must be called after register_default_headers() has already added the
1234
 * header you want to remove.
1235
 *
1236
 * @see register_default_headers()
1237
 * @since 3.0.0
1238
 *
1239
 * @param string|array $header The header string id (key of array) to remove, or an array thereof.
1240
 * @return bool|void A single header returns true on success, false on failure.
1241
 *                   There is currently no return value for multiple headers.
1242
 */
1243
function unregister_default_headers( $header ) {
1244
	global $_wp_default_headers;
1245
	if ( is_array( $header ) ) {
1246
		array_map( 'unregister_default_headers', $header );
1247
	} elseif ( isset( $_wp_default_headers[ $header ] ) ) {
1248
		unset( $_wp_default_headers[ $header ] );
1249
		return true;
1250
	} else {
1251
		return false;
1252
	}
1253
}
1254
1255
/**
1256
 * Retrieve background image for custom background.
1257
 *
1258
 * @since 3.0.0
1259
 *
1260
 * @return string
1261
 */
1262
function get_background_image() {
1263
	return get_theme_mod('background_image', get_theme_support( 'custom-background', 'default-image' ) );
1264
}
1265
1266
/**
1267
 * Display background image path.
1268
 *
1269
 * @since 3.0.0
1270
 */
1271
function background_image() {
1272
	echo get_background_image();
1273
}
1274
1275
/**
1276
 * Retrieve value for custom background color.
1277
 *
1278
 * @since 3.0.0
1279
 *
1280
 * @return string
1281
 */
1282
function get_background_color() {
1283
	return get_theme_mod('background_color', get_theme_support( 'custom-background', 'default-color' ) );
1284
}
1285
1286
/**
1287
 * Display background color value.
1288
 *
1289
 * @since 3.0.0
1290
 */
1291
function background_color() {
1292
	echo get_background_color();
1293
}
1294
1295
/**
1296
 * Default custom background callback.
1297
 *
1298
 * @since 3.0.0
1299
 * @access protected
1300
 */
1301
function _custom_background_cb() {
1302
	// $background is the saved custom image, or the default image.
1303
	$background = set_url_scheme( get_background_image() );
1304
1305
	// $color is the saved custom color.
1306
	// A default has to be specified in style.css. It will not be printed here.
1307
	$color = get_background_color();
1308
1309
	if ( $color === get_theme_support( 'custom-background', 'default-color' ) ) {
1310
		$color = false;
1311
	}
1312
1313
	if ( ! $background && ! $color )
1314
		return;
1315
1316
	$style = $color ? "background-color: #$color;" : '';
1317
1318
	if ( $background ) {
1319
		$image = " background-image: url('$background');";
1320
1321
		$repeat = get_theme_mod( 'background_repeat', get_theme_support( 'custom-background', 'default-repeat' ) );
1322
		if ( ! in_array( $repeat, array( 'no-repeat', 'repeat-x', 'repeat-y', 'repeat' ) ) )
1323
			$repeat = 'repeat';
1324
		$repeat = " background-repeat: $repeat;";
1325
1326
		$position = get_theme_mod( 'background_position_x', get_theme_support( 'custom-background', 'default-position-x' ) );
1327
		if ( ! in_array( $position, array( 'center', 'right', 'left' ) ) )
1328
			$position = 'left';
1329
		$position = " background-position: top $position;";
1330
1331
		$attachment = get_theme_mod( 'background_attachment', get_theme_support( 'custom-background', 'default-attachment' ) );
1332
		if ( ! in_array( $attachment, array( 'fixed', 'scroll' ) ) )
1333
			$attachment = 'scroll';
1334
		$attachment = " background-attachment: $attachment;";
1335
1336
		$style .= $image . $repeat . $position . $attachment;
1337
	}
1338
?>
1339
<style type="text/css" id="custom-background-css">
1340
body.custom-background { <?php echo trim( $style ); ?> }
1341
</style>
1342
<?php
1343
}
1344
1345
/**
1346
 * Add callback for custom TinyMCE editor stylesheets.
1347
 *
1348
 * The parameter $stylesheet is the name of the stylesheet, relative to
1349
 * the theme root. It also accepts an array of stylesheets.
1350
 * It is optional and defaults to 'editor-style.css'.
1351
 *
1352
 * This function automatically adds another stylesheet with -rtl prefix, e.g. editor-style-rtl.css.
1353
 * If that file doesn't exist, it is removed before adding the stylesheet(s) to TinyMCE.
1354
 * If an array of stylesheets is passed to add_editor_style(),
1355
 * RTL is only added for the first stylesheet.
1356
 *
1357
 * Since version 3.4 the TinyMCE body has .rtl CSS class.
1358
 * It is a better option to use that class and add any RTL styles to the main stylesheet.
1359
 *
1360
 * @since 3.0.0
1361
 *
1.1.1 by Nick Moffitt
New Upstream Version 4.1
1362
 * @param array|string $stylesheet Optional. Stylesheet name or array thereof, relative to theme root.
1 by Jacek Nykis
Initial commit
1363
 * 	Defaults to 'editor-style.css'
1364
 */
1365
function add_editor_style( $stylesheet = 'editor-style.css' ) {
1366
1367
	add_theme_support( 'editor-style' );
1368
1369
	if ( ! is_admin() )
1370
		return;
1371
1372
	global $editor_styles;
1373
	$editor_styles = (array) $editor_styles;
1374
	$stylesheet    = (array) $stylesheet;
1375
	if ( is_rtl() ) {
1376
		$rtl_stylesheet = str_replace('.css', '-rtl.css', $stylesheet[0]);
1377
		$stylesheet[] = $rtl_stylesheet;
1378
	}
1379
1380
	$editor_styles = array_merge( $editor_styles, $stylesheet );
1381
}
1382
1383
/**
1384
 * Removes all visual editor stylesheets.
1385
 *
1386
 * @since 3.1.0
1387
 *
1388
 * @return bool True on success, false if there were no stylesheets to remove.
1389
 */
1390
function remove_editor_styles() {
1391
	if ( ! current_theme_supports( 'editor-style' ) )
1392
		return false;
1393
	_remove_theme_support( 'editor-style' );
1394
	if ( is_admin() )
1395
		$GLOBALS['editor_styles'] = array();
1396
	return true;
1397
}
1398
1399
/**
1400
 * Retrieve any registered editor stylesheets
1401
 *
1402
 * @since 4.0.0
1403
 *
1404
 * @global $editor_styles Registered editor stylesheets
1405
 *
1406
 * @return array If registered, a list of editor stylesheet URLs.
1407
 */
1408
function get_editor_stylesheets() {
1409
	$stylesheets = array();
1410
	// load editor_style.css if the current theme supports it
1411
	if ( ! empty( $GLOBALS['editor_styles'] ) && is_array( $GLOBALS['editor_styles'] ) ) {
1412
		$editor_styles = $GLOBALS['editor_styles'];
1413
1414
		$editor_styles = array_unique( array_filter( $editor_styles ) );
1415
		$style_uri = get_stylesheet_directory_uri();
1416
		$style_dir = get_stylesheet_directory();
1417
1418
		// Support externally referenced styles (like, say, fonts).
1419
		foreach ( $editor_styles as $key => $file ) {
1420
			if ( preg_match( '~^(https?:)?//~', $file ) ) {
1421
				$stylesheets[] = esc_url_raw( $file );
1422
				unset( $editor_styles[ $key ] );
1423
			}
1424
		}
1425
1426
		// Look in a parent theme first, that way child theme CSS overrides.
1427
		if ( is_child_theme() ) {
1428
			$template_uri = get_template_directory_uri();
1429
			$template_dir = get_template_directory();
1430
1431
			foreach ( $editor_styles as $key => $file ) {
1432
				if ( $file && file_exists( "$template_dir/$file" ) ) {
1433
					$stylesheets[] = "$template_uri/$file";
1434
				}
1435
			}
1436
		}
1437
1438
		foreach ( $editor_styles as $file ) {
1439
			if ( $file && file_exists( "$style_dir/$file" ) ) {
1440
				$stylesheets[] = "$style_uri/$file";
1441
			}
1442
		}
1443
	}
1444
	return $stylesheets;
1445
}
1446
1447
/**
1448
 * Allows a theme to register its support of a certain feature
1449
 *
1450
 * Must be called in the theme's functions.php file to work.
1451
 * If attached to a hook, it must be after_setup_theme.
1452
 * The init hook may be too late for some features.
1453
 *
1454
 * @since 2.9.0
1455
 *
1456
 * @param string $feature The feature being added.
1457
 * @return void|bool False on failure, void otherwise.
1458
 */
1459
function add_theme_support( $feature ) {
1460
	global $_wp_theme_features;
1461
1462
	if ( func_num_args() == 1 )
1463
		$args = true;
1464
	else
1465
		$args = array_slice( func_get_args(), 1 );
1466
1467
	switch ( $feature ) {
1468
		case 'post-formats' :
1.1.1 by Nick Moffitt
New Upstream Version 4.1
1469
			if ( is_array( $args[0] ) ) {
1470
				$post_formats = get_post_format_slugs();
1471
				unset( $post_formats['standard'] );
1472
1473
				$args[0] = array_intersect( $args[0], array_keys( $post_formats ) );
1474
			}
1 by Jacek Nykis
Initial commit
1475
			break;
1476
1477
		case 'html5' :
1478
			// You can't just pass 'html5', you need to pass an array of types.
1479
			if ( empty( $args[0] ) ) {
1480
				// Build an array of types for back-compat.
1481
				$args = array( 0 => array( 'comment-list', 'comment-form', 'search-form' ) );
1482
			} elseif ( ! is_array( $args[0] ) ) {
1.1.1 by Nick Moffitt
New Upstream Version 4.1
1483
				_doing_it_wrong( "add_theme_support( 'html5' )", __( 'You need to pass an array of types.' ), '3.6.1' );
1 by Jacek Nykis
Initial commit
1484
				return false;
1485
			}
1486
1487
			// Calling 'html5' again merges, rather than overwrites.
1488
			if ( isset( $_wp_theme_features['html5'] ) )
1489
				$args[0] = array_merge( $_wp_theme_features['html5'][0], $args[0] );
1490
			break;
1491
1492
		case 'custom-header-uploads' :
1493
			return add_theme_support( 'custom-header', array( 'uploads' => true ) );
1494
1495
		case 'custom-header' :
1496
			if ( ! is_array( $args ) )
1497
				$args = array( 0 => array() );
1498
1499
			$defaults = array(
1500
				'default-image' => '',
1501
				'random-default' => false,
1502
				'width' => 0,
1503
				'height' => 0,
1504
				'flex-height' => false,
1505
				'flex-width' => false,
1506
				'default-text-color' => '',
1507
				'header-text' => true,
1508
				'uploads' => true,
1509
				'wp-head-callback' => '',
1510
				'admin-head-callback' => '',
1511
				'admin-preview-callback' => '',
1512
			);
1513
1514
			$jit = isset( $args[0]['__jit'] );
1515
			unset( $args[0]['__jit'] );
1516
1517
			// Merge in data from previous add_theme_support() calls.
1518
			// The first value registered wins. (A child theme is set up first.)
1519
			if ( isset( $_wp_theme_features['custom-header'] ) )
1520
				$args[0] = wp_parse_args( $_wp_theme_features['custom-header'][0], $args[0] );
1521
1522
			// Load in the defaults at the end, as we need to insure first one wins.
1523
			// This will cause all constants to be defined, as each arg will then be set to the default.
1524
			if ( $jit )
1525
				$args[0] = wp_parse_args( $args[0], $defaults );
1526
1527
			// If a constant was defined, use that value. Otherwise, define the constant to ensure
1528
			// the constant is always accurate (and is not defined later,  overriding our value).
1529
			// As stated above, the first value wins.
1530
			// Once we get to wp_loaded (just-in-time), define any constants we haven't already.
1531
			// Constants are lame. Don't reference them. This is just for backwards compatibility.
1532
1533
			if ( defined( 'NO_HEADER_TEXT' ) )
1534
				$args[0]['header-text'] = ! NO_HEADER_TEXT;
1535
			elseif ( isset( $args[0]['header-text'] ) )
1536
				define( 'NO_HEADER_TEXT', empty( $args[0]['header-text'] ) );
1537
1538
			if ( defined( 'HEADER_IMAGE_WIDTH' ) )
1539
				$args[0]['width'] = (int) HEADER_IMAGE_WIDTH;
1540
			elseif ( isset( $args[0]['width'] ) )
1541
				define( 'HEADER_IMAGE_WIDTH', (int) $args[0]['width'] );
1542
1543
			if ( defined( 'HEADER_IMAGE_HEIGHT' ) )
1544
				$args[0]['height'] = (int) HEADER_IMAGE_HEIGHT;
1545
			elseif ( isset( $args[0]['height'] ) )
1546
				define( 'HEADER_IMAGE_HEIGHT', (int) $args[0]['height'] );
1547
1548
			if ( defined( 'HEADER_TEXTCOLOR' ) )
1549
				$args[0]['default-text-color'] = HEADER_TEXTCOLOR;
1550
			elseif ( isset( $args[0]['default-text-color'] ) )
1551
				define( 'HEADER_TEXTCOLOR', $args[0]['default-text-color'] );
1552
1553
			if ( defined( 'HEADER_IMAGE' ) )
1554
				$args[0]['default-image'] = HEADER_IMAGE;
1555
			elseif ( isset( $args[0]['default-image'] ) )
1556
				define( 'HEADER_IMAGE', $args[0]['default-image'] );
1557
1558
			if ( $jit && ! empty( $args[0]['default-image'] ) )
1559
				$args[0]['random-default'] = false;
1560
1561
			// If headers are supported, and we still don't have a defined width or height,
1562
			// we have implicit flex sizes.
1563
			if ( $jit ) {
1564
				if ( empty( $args[0]['width'] ) && empty( $args[0]['flex-width'] ) )
1565
					$args[0]['flex-width'] = true;
1566
				if ( empty( $args[0]['height'] ) && empty( $args[0]['flex-height'] ) )
1567
					$args[0]['flex-height'] = true;
1568
			}
1569
1570
			break;
1571
1572
		case 'custom-background' :
1573
			if ( ! is_array( $args ) )
1574
				$args = array( 0 => array() );
1575
1576
			$defaults = array(
1577
				'default-image'          => '',
1578
				'default-repeat'         => 'repeat',
1579
				'default-position-x'     => 'left',
1580
				'default-attachment'     => 'scroll',
1581
				'default-color'          => '',
1582
				'wp-head-callback'       => '_custom_background_cb',
1583
				'admin-head-callback'    => '',
1584
				'admin-preview-callback' => '',
1585
			);
1586
1587
			$jit = isset( $args[0]['__jit'] );
1588
			unset( $args[0]['__jit'] );
1589
1590
			// Merge in data from previous add_theme_support() calls. The first value registered wins.
1591
			if ( isset( $_wp_theme_features['custom-background'] ) )
1592
				$args[0] = wp_parse_args( $_wp_theme_features['custom-background'][0], $args[0] );
1593
1594
			if ( $jit )
1595
				$args[0] = wp_parse_args( $args[0], $defaults );
1596
1597
			if ( defined( 'BACKGROUND_COLOR' ) )
1598
				$args[0]['default-color'] = BACKGROUND_COLOR;
1599
			elseif ( isset( $args[0]['default-color'] ) || $jit )
1600
				define( 'BACKGROUND_COLOR', $args[0]['default-color'] );
1601
1602
			if ( defined( 'BACKGROUND_IMAGE' ) )
1603
				$args[0]['default-image'] = BACKGROUND_IMAGE;
1604
			elseif ( isset( $args[0]['default-image'] ) || $jit )
1605
				define( 'BACKGROUND_IMAGE', $args[0]['default-image'] );
1606
1607
			break;
1.1.1 by Nick Moffitt
New Upstream Version 4.1
1608
1609
		// Ensure that 'title-tag' is accessible in the admin.
1610
		case 'title-tag' :
1611
			// Can be called in functions.php but must happen before wp_loaded, i.e. not in header.php.
1612
			if ( did_action( 'wp_loaded' ) ) {
1613
				/* translators: 1: Theme support 2: hook name */
1614
				_doing_it_wrong( "add_theme_support( 'title-tag' )", sprintf( __( 'Theme support for %1$s should be registered before the %2$s hook.' ),
1615
					'<code>title-tag</code>', '<code>wp_loaded</code>' ), '4.1' );
1616
1617
				return false;
1618
			}
1 by Jacek Nykis
Initial commit
1619
	}
1620
1621
	$_wp_theme_features[ $feature ] = $args;
1622
}
1623
1624
/**
1625
 * Registers the internal custom header and background routines.
1626
 *
1627
 * @since 3.4.0
1628
 * @access private
1629
 */
1630
function _custom_header_background_just_in_time() {
1631
	global $custom_image_header, $custom_background;
1632
1633
	if ( current_theme_supports( 'custom-header' ) ) {
1634
		// In case any constants were defined after an add_custom_image_header() call, re-run.
1635
		add_theme_support( 'custom-header', array( '__jit' => true ) );
1636
1637
		$args = get_theme_support( 'custom-header' );
1638
		if ( $args[0]['wp-head-callback'] )
1639
			add_action( 'wp_head', $args[0]['wp-head-callback'] );
1640
1641
		if ( is_admin() ) {
1642
			require_once( ABSPATH . 'wp-admin/custom-header.php' );
1643
			$custom_image_header = new Custom_Image_Header( $args[0]['admin-head-callback'], $args[0]['admin-preview-callback'] );
1644
		}
1645
	}
1646
1647
	if ( current_theme_supports( 'custom-background' ) ) {
1648
		// In case any constants were defined after an add_custom_background() call, re-run.
1649
		add_theme_support( 'custom-background', array( '__jit' => true ) );
1650
1651
		$args = get_theme_support( 'custom-background' );
1652
		add_action( 'wp_head', $args[0]['wp-head-callback'] );
1653
1654
		if ( is_admin() ) {
1655
			require_once( ABSPATH . 'wp-admin/custom-background.php' );
1656
			$custom_background = new Custom_Background( $args[0]['admin-head-callback'], $args[0]['admin-preview-callback'] );
1657
		}
1658
	}
1659
}
1660
1661
/**
1662
 * Gets the theme support arguments passed when registering that support
1663
 *
1664
 * @since 3.1.0
1665
 *
1666
 * @param string $feature the feature to check
1.1.1 by Nick Moffitt
New Upstream Version 4.1
1667
 * @return mixed The array of extra arguments or the value for the registered feature.
1 by Jacek Nykis
Initial commit
1668
 */
1669
function get_theme_support( $feature ) {
1670
	global $_wp_theme_features;
1671
	if ( ! isset( $_wp_theme_features[ $feature ] ) )
1672
		return false;
1673
1674
	if ( func_num_args() <= 1 )
1675
		return $_wp_theme_features[ $feature ];
1676
1677
	$args = array_slice( func_get_args(), 1 );
1678
	switch ( $feature ) {
1679
		case 'custom-header' :
1680
		case 'custom-background' :
1681
			if ( isset( $_wp_theme_features[ $feature ][0][ $args[0] ] ) )
1682
				return $_wp_theme_features[ $feature ][0][ $args[0] ];
1683
			return false;
1684
1685
		default :
1686
			return $_wp_theme_features[ $feature ];
1687
	}
1688
}
1689
1690
/**
1691
 * Allows a theme to de-register its support of a certain feature
1692
 *
1693
 * Should be called in the theme's functions.php file. Generally would
1694
 * be used for child themes to override support from the parent theme.
1695
 *
1696
 * @since 3.0.0
1697
 * @see add_theme_support()
1698
 * @param string $feature the feature being added
1.1.1 by Nick Moffitt
New Upstream Version 4.1
1699
 * @return null|bool Whether feature was removed.
1 by Jacek Nykis
Initial commit
1700
 */
1701
function remove_theme_support( $feature ) {
1702
	// Blacklist: for internal registrations not used directly by themes.
1703
	if ( in_array( $feature, array( 'editor-style', 'widgets', 'menus' ) ) )
1704
		return false;
1705
1706
	return _remove_theme_support( $feature );
1707
}
1708
1709
/**
1710
 * Do not use. Removes theme support internally, ignorant of the blacklist.
1711
 *
1712
 * @access private
1713
 * @since 3.1.0
1.1.1 by Nick Moffitt
New Upstream Version 4.1
1714
 * @param string $feature
1 by Jacek Nykis
Initial commit
1715
 */
1716
function _remove_theme_support( $feature ) {
1717
	global $_wp_theme_features;
1718
1719
	switch ( $feature ) {
1720
		case 'custom-header-uploads' :
1721
			if ( ! isset( $_wp_theme_features['custom-header'] ) )
1722
				return false;
1723
			add_theme_support( 'custom-header', array( 'uploads' => false ) );
1724
			return; // Do not continue - custom-header-uploads no longer exists.
1725
	}
1726
1727
	if ( ! isset( $_wp_theme_features[ $feature ] ) )
1728
		return false;
1729
1730
	switch ( $feature ) {
1731
		case 'custom-header' :
1732
			if ( ! did_action( 'wp_loaded' ) )
1733
				break;
1734
			$support = get_theme_support( 'custom-header' );
1735
			if ( $support[0]['wp-head-callback'] )
1736
				remove_action( 'wp_head', $support[0]['wp-head-callback'] );
1737
			remove_action( 'admin_menu', array( $GLOBALS['custom_image_header'], 'init' ) );
1738
			unset( $GLOBALS['custom_image_header'] );
1739
			break;
1740
1741
		case 'custom-background' :
1742
			if ( ! did_action( 'wp_loaded' ) )
1743
				break;
1744
			$support = get_theme_support( 'custom-background' );
1745
			remove_action( 'wp_head', $support[0]['wp-head-callback'] );
1746
			remove_action( 'admin_menu', array( $GLOBALS['custom_background'], 'init' ) );
1747
			unset( $GLOBALS['custom_background'] );
1748
			break;
1749
	}
1750
1751
	unset( $_wp_theme_features[ $feature ] );
1752
	return true;
1753
}
1754
1755
/**
1756
 * Checks a theme's support for a given feature
1757
 *
1758
 * @since 2.9.0
1759
 * @param string $feature the feature being checked
1760
 * @return boolean
1761
 */
1762
function current_theme_supports( $feature ) {
1763
	global $_wp_theme_features;
1764
1765
	if ( 'custom-header-uploads' == $feature )
1766
		return current_theme_supports( 'custom-header', 'uploads' );
1767
1768
	if ( !isset( $_wp_theme_features[$feature] ) )
1769
		return false;
1770
1.1.1 by Nick Moffitt
New Upstream Version 4.1
1771
	if ( 'title-tag' == $feature ) {
1772
		// Don't confirm support unless called internally.
1773
		$trace = debug_backtrace();
1774
		if ( ! in_array( $trace[1]['function'], array( '_wp_render_title_tag', 'wp_title' ) ) ) {
1775
			return false;
1776
		}
1777
	}
1778
1 by Jacek Nykis
Initial commit
1779
	// If no args passed then no extra checks need be performed
1780
	if ( func_num_args() <= 1 )
1781
		return true;
1782
1783
	$args = array_slice( func_get_args(), 1 );
1784
1785
	switch ( $feature ) {
1786
		case 'post-thumbnails':
1787
			// post-thumbnails can be registered for only certain content/post types by passing
1788
			// an array of types to add_theme_support(). If no array was passed, then
1789
			// any type is accepted
1790
			if ( true === $_wp_theme_features[$feature] )  // Registered for all types
1791
				return true;
1792
			$content_type = $args[0];
1793
			return in_array( $content_type, $_wp_theme_features[$feature][0] );
1794
1795
		case 'html5':
1796
		case 'post-formats':
1797
			// specific post formats can be registered by passing an array of types to
1798
			// add_theme_support()
1799
1800
			// Specific areas of HTML5 support *must* be passed via an array to add_theme_support()
1801
1802
			$type = $args[0];
1803
			return in_array( $type, $_wp_theme_features[$feature][0] );
1804
1805
		case 'custom-header':
1806
		case 'custom-background' :
1807
			// specific custom header and background capabilities can be registered by passing
1808
			// an array to add_theme_support()
1809
			$header_support = $args[0];
1810
			return ( isset( $_wp_theme_features[$feature][0][$header_support] ) && $_wp_theme_features[$feature][0][$header_support] );
1811
	}
1812
1813
	/**
1814
	 * Filter whether the current theme supports a specific feature.
1815
	 *
1.1.1 by Nick Moffitt
New Upstream Version 4.1
1816
	 * The dynamic portion of the hook name, `$feature`, refers to the specific theme
1817
	 * feature. Possible values include 'post-formats', 'post-thumbnails', 'custom-background',
1818
	 * 'custom-header', 'menus', 'automatic-feed-links', and 'html5'.
1 by Jacek Nykis
Initial commit
1819
	 *
1820
	 * @since 3.4.0
1821
	 *
1822
	 * @param bool   true     Whether the current theme supports the given feature. Default true.
1823
	 * @param array  $args    Array of arguments for the feature.
1824
	 * @param string $feature The theme feature.
1825
	 */
1826
	return apply_filters( "current_theme_supports-{$feature}", true, $args, $_wp_theme_features[$feature] );
1827
}
1828
1829
/**
1830
 * Checks a theme's support for a given feature before loading the functions which implement it.
1831
 *
1832
 * @since 2.9.0
1833
 *
1834
 * @param string $feature The feature being checked.
1835
 * @param string $include Path to the file.
1836
 * @return bool True if the current theme supports the supplied feature, false otherwise.
1837
 */
1838
function require_if_theme_supports( $feature, $include ) {
1839
	if ( current_theme_supports( $feature ) ) {
1840
		require ( $include );
1841
		return true;
1842
	}
1843
	return false;
1844
}
1845
1846
/**
1847
 * Checks an attachment being deleted to see if it's a header or background image.
1848
 *
1849
 * If true it removes the theme modification which would be pointing at the deleted
1850
 * attachment
1851
 *
1852
 * @access private
1853
 * @since 3.0.0
1854
 * @param int $id the attachment id
1855
 */
1856
function _delete_attachment_theme_mod( $id ) {
1857
	$attachment_image = wp_get_attachment_url( $id );
1858
	$header_image = get_header_image();
1859
	$background_image = get_background_image();
1860
1861
	if ( $header_image && $header_image == $attachment_image )
1862
		remove_theme_mod( 'header_image' );
1863
1864
	if ( $background_image && $background_image == $attachment_image )
1865
		remove_theme_mod( 'background_image' );
1866
}
1867
1868
/**
1869
 * Checks if a theme has been changed and runs 'after_switch_theme' hook on the next WP load
1870
 *
1871
 * @since 3.3.0
1872
 */
1873
function check_theme_switched() {
1874
	if ( $stylesheet = get_option( 'theme_switched' ) ) {
1875
		$old_theme = wp_get_theme( $stylesheet );
1876
1877
		// Prevent retrieve_widgets() from running since Customizer already called it up front
1878
		if ( get_option( 'theme_switched_via_customizer' ) ) {
1879
			remove_action( 'after_switch_theme', '_wp_sidebars_changed' );
1880
			update_option( 'theme_switched_via_customizer', false );
1881
		}
1882
1883
		if ( $old_theme->exists() ) {
1884
			/**
1885
			 * Fires on the first WP load after a theme switch if the old theme still exists.
1886
			 *
1887
			 * This action fires multiple times and the parameters differs
1888
			 * according to the context, if the old theme exists or not.
1889
			 * If the old theme is missing, the parameter will be the slug
1890
			 * of the old theme.
1891
			 *
1892
			 * @since 3.3.0
1893
			 *
1894
			 * @param string   $old_name  Old theme name.
1895
			 * @param WP_Theme $old_theme WP_Theme instance of the old theme.
1896
			 */
1897
			do_action( 'after_switch_theme', $old_theme->get( 'Name' ), $old_theme );
1898
		} else {
1899
			/** This action is documented in wp-includes/theme.php */
1900
			do_action( 'after_switch_theme', $stylesheet );
1901
		}
1902
1903
		update_option( 'theme_switched', false );
1904
	}
1905
}
1906
1907
/**
1908
 * Includes and instantiates the WP_Customize_Manager class.
1909
 *
1910
 * Fires when ?wp_customize=on or on wp-admin/customize.php.
1911
 *
1912
 * @since 3.4.0
1913
 */
1914
function _wp_customize_include() {
1915
	if ( ! ( ( isset( $_REQUEST['wp_customize'] ) && 'on' == $_REQUEST['wp_customize'] )
1916
		|| ( is_admin() && 'customize.php' == basename( $_SERVER['PHP_SELF'] ) )
1917
	) )
1918
		return;
1919
1920
	require( ABSPATH . WPINC . '/class-wp-customize-manager.php' );
1921
	// Init Customize class
1922
	$GLOBALS['wp_customize'] = new WP_Customize_Manager;
1923
}
1924
1925
/**
1926
 * Adds settings for the customize-loader script.
1927
 *
1928
 * @since 3.4.0
1929
 */
1930
function _wp_customize_loader_settings() {
1931
	global $wp_scripts;
1932
1933
	$admin_origin = parse_url( admin_url() );
1934
	$home_origin  = parse_url( home_url() );
1935
	$cross_domain = ( strtolower( $admin_origin[ 'host' ] ) != strtolower( $home_origin[ 'host' ] ) );
1936
1937
	$browser = array(
1938
		'mobile' => wp_is_mobile(),
1939
		'ios'    => wp_is_mobile() && preg_match( '/iPad|iPod|iPhone/', $_SERVER['HTTP_USER_AGENT'] ),
1940
	);
1941
1942
	$settings = array(
1943
		'url'           => esc_url( admin_url( 'customize.php' ) ),
1944
		'isCrossDomain' => $cross_domain,
1945
		'browser'       => $browser,
1946
		'l10n'          => array(
1.1.4 by Paul Gear
new upstream release 4.2
1947
			'saveAlert'       => __( 'The changes you made will be lost if you navigate away from this page.' ),
1948
			'mainIframeTitle' => __( 'Customizer' ),
1 by Jacek Nykis
Initial commit
1949
		),
1950
	);
1951
1.1.1 by Nick Moffitt
New Upstream Version 4.1
1952
	$script = 'var _wpCustomizeLoaderSettings = ' . wp_json_encode( $settings ) . ';';
1 by Jacek Nykis
Initial commit
1953
1954
	$data = $wp_scripts->get_data( 'customize-loader', 'data' );
1955
	if ( $data )
1956
		$script = "$data\n$script";
1957
1958
	$wp_scripts->add_data( 'customize-loader', 'data', $script );
1959
}
1960
1961
/**
1.1.1 by Nick Moffitt
New Upstream Version 4.1
1962
 * Returns a URL to load the Customizer.
1 by Jacek Nykis
Initial commit
1963
 *
1964
 * @since 3.4.0
1965
 *
1966
 * @param string $stylesheet Optional. Theme to customize. Defaults to current theme.
1967
 * 	The theme's stylesheet will be urlencoded if necessary.
1968
 */
1969
function wp_customize_url( $stylesheet = null ) {
1970
	$url = admin_url( 'customize.php' );
1971
	if ( $stylesheet )
1972
		$url .= '?theme=' . urlencode( $stylesheet );
1973
	return esc_url( $url );
1974
}
1975
1976
/**
1.1.1 by Nick Moffitt
New Upstream Version 4.1
1977
 * Prints a script to check whether or not the Customizer is supported,
1 by Jacek Nykis
Initial commit
1978
 * and apply either the no-customize-support or customize-support class
1979
 * to the body.
1980
 *
1981
 * This function MUST be called inside the body tag.
1982
 *
1983
 * Ideally, call this function immediately after the body tag is opened.
1984
 * This prevents a flash of unstyled content.
1985
 *
1986
 * It is also recommended that you add the "no-customize-support" class
1987
 * to the body tag by default.
1988
 *
1989
 * @since 3.4.0
1990
 */
1991
function wp_customize_support_script() {
1992
	$admin_origin = parse_url( admin_url() );
1993
	$home_origin  = parse_url( home_url() );
1994
	$cross_domain = ( strtolower( $admin_origin[ 'host' ] ) != strtolower( $home_origin[ 'host' ] ) );
1995
1996
	?>
1997
	<script type="text/javascript">
1998
		(function() {
1999
			var request, b = document.body, c = 'className', cs = 'customize-support', rcs = new RegExp('(^|\\s+)(no-)?'+cs+'(\\s+|$)');
2000
2001
<?php		if ( $cross_domain ): ?>
2002
			request = (function(){ var xhr = new XMLHttpRequest(); return ('withCredentials' in xhr); })();
2003
<?php		else: ?>
2004
			request = true;
2005
<?php		endif; ?>
2006
2007
			b[c] = b[c].replace( rcs, ' ' );
2008
			b[c] += ( window.postMessage && request ? ' ' : ' no-' ) + cs;
2009
		}());
2010
	</script>
2011
	<?php
2012
}
2013
2014
/**
2015
 * Whether the site is being previewed in the Customizer.
2016
 *
2017
 * @since 4.0.0
2018
 *
2019
 * @global WP_Customize_Manager $wp_customize Customizer instance.
2020
 *
2021
 * @return bool True if the site is being previewed in the Customizer, false otherwise.
2022
 */
2023
function is_customize_preview() {
2024
	global $wp_customize;
2025
1.1.4 by Paul Gear
new upstream release 4.2
2026
	return ( $wp_customize instanceof WP_Customize_Manager ) && $wp_customize->is_preview();
1 by Jacek Nykis
Initial commit
2027
}