/wp-includes/theme.php

  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. if ( empty( $theme_directories ) ) 
  49. return array(); 
  50.  
  51. if ( is_multisite() && null !== $args['allowed'] ) { 
  52. $allowed = $args['allowed']; 
  53. if ( 'network' === $allowed ) 
  54. $theme_directories = array_intersect_key( $theme_directories, WP_Theme::get_allowed_on_network() ); 
  55. elseif ( 'site' === $allowed ) 
  56. $theme_directories = array_intersect_key( $theme_directories, WP_Theme::get_allowed_on_site( $args['blog_id'] ) ); 
  57. elseif ( $allowed ) 
  58. $theme_directories = array_intersect_key( $theme_directories, WP_Theme::get_allowed( $args['blog_id'] ) ); 
  59. else 
  60. $theme_directories = array_diff_key( $theme_directories, WP_Theme::get_allowed( $args['blog_id'] ) ); 
  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. if ( null !== $args['errors'] ) { 
  72. foreach ( $themes as $theme => $wp_theme ) { 
  73. if ( $wp_theme->errors() != $args['errors'] ) 
  74. unset( $themes[ $theme ] ); 
  75.  
  76. return $themes; 
  77.  
  78. /** 
  79. * Gets a WP_Theme object for a theme. 
  80. * 
  81. * @since 3.4.0 
  82. * 
  83. * @global array $wp_theme_directories 
  84. * 
  85. * @param string $stylesheet Directory name for the theme. Optional. Defaults to current theme. 
  86. * @param string $theme_root Absolute path of the theme root to look in. Optional. If not specified, get_raw_theme_root() 
  87. * is used to calculate the theme root for the $stylesheet provided (or current theme). 
  88. * @return WP_Theme Theme object. Be sure to check the object's exists() method if you need to confirm the theme's existence. 
  89. */ 
  90. function wp_get_theme( $stylesheet = null, $theme_root = null ) { 
  91. global $wp_theme_directories; 
  92.  
  93. if ( empty( $stylesheet ) ) 
  94. $stylesheet = get_stylesheet(); 
  95.  
  96. if ( empty( $theme_root ) ) { 
  97. $theme_root = get_raw_theme_root( $stylesheet ); 
  98. if ( false === $theme_root ) 
  99. $theme_root = WP_CONTENT_DIR . '/themes'; 
  100. elseif ( ! in_array( $theme_root, (array) $wp_theme_directories ) ) 
  101. $theme_root = WP_CONTENT_DIR . $theme_root; 
  102.  
  103. return new WP_Theme( $stylesheet, $theme_root ); 
  104.  
  105. /** 
  106. * Clears the cache held by get_theme_roots() and WP_Theme. 
  107. * 
  108. * @since 3.5.0 
  109. * @param bool $clear_update_cache Whether to clear the Theme updates cache 
  110. */ 
  111. function wp_clean_themes_cache( $clear_update_cache = true ) { 
  112. if ( $clear_update_cache ) 
  113. delete_site_transient( 'update_themes' ); 
  114. search_theme_directories( true ); 
  115. foreach ( wp_get_themes( array( 'errors' => null ) ) as $theme ) 
  116. $theme->cache_delete(); 
  117.  
  118. /** 
  119. * Whether a child theme is in use. 
  120. * 
  121. * @since 3.0.0 
  122. * 
  123. * @return bool true if a child theme is in use, false otherwise. 
  124. **/ 
  125. function is_child_theme() { 
  126. return ( TEMPLATEPATH !== STYLESHEETPATH ); 
  127.  
  128. /** 
  129. * Retrieve name of the current stylesheet. 
  130. * 
  131. * The theme name that the administrator has currently set the front end theme 
  132. * as. 
  133. * 
  134. * For all intents and purposes, the template name and the stylesheet name are 
  135. * going to be the same for most cases. 
  136. * 
  137. * @since 1.5.0 
  138. * 
  139. * @return string Stylesheet name. 
  140. */ 
  141. function get_stylesheet() { 
  142. /** 
  143. * Filters the name of current stylesheet. 
  144. * 
  145. * @since 1.5.0 
  146. * 
  147. * @param string $stylesheet Name of the current stylesheet. 
  148. */ 
  149. return apply_filters( 'stylesheet', get_option( 'stylesheet' ) ); 
  150.  
  151. /** 
  152. * Retrieve stylesheet directory path for current theme. 
  153. * 
  154. * @since 1.5.0 
  155. * 
  156. * @return string Path to current theme directory. 
  157. */ 
  158. function get_stylesheet_directory() { 
  159. $stylesheet = get_stylesheet(); 
  160. $theme_root = get_theme_root( $stylesheet ); 
  161. $stylesheet_dir = "$theme_root/$stylesheet"; 
  162.  
  163. /** 
  164. * Filters the stylesheet directory path for current theme. 
  165. * 
  166. * @since 1.5.0 
  167. * 
  168. * @param string $stylesheet_dir Absolute path to the current theme. 
  169. * @param string $stylesheet Directory name of the current theme. 
  170. * @param string $theme_root Absolute path to themes directory. 
  171. */ 
  172. return apply_filters( 'stylesheet_directory', $stylesheet_dir, $stylesheet, $theme_root ); 
  173.  
  174. /** 
  175. * Retrieve stylesheet directory URI. 
  176. * 
  177. * @since 1.5.0 
  178. * 
  179. * @return string 
  180. */ 
  181. function get_stylesheet_directory_uri() { 
  182. $stylesheet = str_replace( '%2F', '/', rawurlencode( get_stylesheet() ) ); 
  183. $theme_root_uri = get_theme_root_uri( $stylesheet ); 
  184. $stylesheet_dir_uri = "$theme_root_uri/$stylesheet"; 
  185.  
  186. /** 
  187. * Filters the stylesheet directory URI. 
  188. * 
  189. * @since 1.5.0 
  190. * 
  191. * @param string $stylesheet_dir_uri Stylesheet directory URI. 
  192. * @param string $stylesheet Name of the activated theme's directory. 
  193. * @param string $theme_root_uri Themes root URI. 
  194. */ 
  195. return apply_filters( 'stylesheet_directory_uri', $stylesheet_dir_uri, $stylesheet, $theme_root_uri ); 
  196.  
  197. /** 
  198. * Retrieves the URI of current theme stylesheet. 
  199. * 
  200. * The stylesheet file name is 'style.css' which is appended to the stylesheet directory URI path. 
  201. * See get_stylesheet_directory_uri(). 
  202. * 
  203. * @since 1.5.0 
  204. * 
  205. * @return string 
  206. */ 
  207. function get_stylesheet_uri() { 
  208. $stylesheet_dir_uri = get_stylesheet_directory_uri(); 
  209. $stylesheet_uri = $stylesheet_dir_uri . '/style.css'; 
  210. /** 
  211. * Filters the URI of the current theme stylesheet. 
  212. * 
  213. * @since 1.5.0 
  214. * 
  215. * @param string $stylesheet_uri Stylesheet URI for the current theme/child theme. 
  216. * @param string $stylesheet_dir_uri Stylesheet directory URI for the current theme/child theme. 
  217. */ 
  218. return apply_filters( 'stylesheet_uri', $stylesheet_uri, $stylesheet_dir_uri ); 
  219.  
  220. /** 
  221. * Retrieves the localized stylesheet URI. 
  222. * 
  223. * The stylesheet directory for the localized stylesheet files are located, by 
  224. * default, in the base theme directory. The name of the locale file will be the 
  225. * locale followed by '.css'. If that does not exist, then the text direction 
  226. * stylesheet will be checked for existence, for example 'ltr.css'. 
  227. * 
  228. * The theme may change the location of the stylesheet directory by either using 
  229. * the {@see 'stylesheet_directory_uri'} or {@see 'locale_stylesheet_uri'} filters. 
  230. * 
  231. * If you want to change the location of the stylesheet files for the entire 
  232. * WordPress workflow, then change the former. If you just have the locale in a 
  233. * separate folder, then change the latter. 
  234. * 
  235. * @since 2.1.0 
  236. * 
  237. * @global WP_Locale $wp_locale 
  238. * 
  239. * @return string 
  240. */ 
  241. function get_locale_stylesheet_uri() { 
  242. global $wp_locale; 
  243. $stylesheet_dir_uri = get_stylesheet_directory_uri(); 
  244. $dir = get_stylesheet_directory(); 
  245. $locale = get_locale(); 
  246. if ( file_exists("$dir/$locale.css") ) 
  247. $stylesheet_uri = "$stylesheet_dir_uri/$locale.css"; 
  248. elseif ( !empty($wp_locale->text_direction) && file_exists("$dir/{$wp_locale->text_direction}.css") ) 
  249. $stylesheet_uri = "$stylesheet_dir_uri/{$wp_locale->text_direction}.css"; 
  250. else 
  251. $stylesheet_uri = ''; 
  252. /** 
  253. * Filters the localized stylesheet URI. 
  254. * 
  255. * @since 2.1.0 
  256. * 
  257. * @param string $stylesheet_uri Localized stylesheet URI. 
  258. * @param string $stylesheet_dir_uri Stylesheet directory URI. 
  259. */ 
  260. return apply_filters( 'locale_stylesheet_uri', $stylesheet_uri, $stylesheet_dir_uri ); 
  261.  
  262. /** 
  263. * Retrieve name of the current theme. 
  264. * 
  265. * @since 1.5.0 
  266. * 
  267. * @return string Template name. 
  268. */ 
  269. function get_template() { 
  270. /** 
  271. * Filters the name of the current theme. 
  272. * 
  273. * @since 1.5.0 
  274. * 
  275. * @param string $template Current theme's directory name. 
  276. */ 
  277. return apply_filters( 'template', get_option( 'template' ) ); 
  278.  
  279. /** 
  280. * Retrieve current theme directory. 
  281. * 
  282. * @since 1.5.0 
  283. * 
  284. * @return string Template directory path. 
  285. */ 
  286. function get_template_directory() { 
  287. $template = get_template(); 
  288. $theme_root = get_theme_root( $template ); 
  289. $template_dir = "$theme_root/$template"; 
  290.  
  291. /** 
  292. * Filters the current theme directory path. 
  293. * 
  294. * @since 1.5.0 
  295. * 
  296. * @param string $template_dir The URI of the current theme directory. 
  297. * @param string $template Directory name of the current theme. 
  298. * @param string $theme_root Absolute path to the themes directory. 
  299. */ 
  300. return apply_filters( 'template_directory', $template_dir, $template, $theme_root ); 
  301.  
  302. /** 
  303. * Retrieve theme directory URI. 
  304. * 
  305. * @since 1.5.0 
  306. * 
  307. * @return string Template directory URI. 
  308. */ 
  309. function get_template_directory_uri() { 
  310. $template = str_replace( '%2F', '/', rawurlencode( get_template() ) ); 
  311. $theme_root_uri = get_theme_root_uri( $template ); 
  312. $template_dir_uri = "$theme_root_uri/$template"; 
  313.  
  314. /** 
  315. * Filters the current theme directory URI. 
  316. * 
  317. * @since 1.5.0 
  318. * 
  319. * @param string $template_dir_uri The URI of the current theme directory. 
  320. * @param string $template Directory name of the current theme. 
  321. * @param string $theme_root_uri The themes root URI. 
  322. */ 
  323. return apply_filters( 'template_directory_uri', $template_dir_uri, $template, $theme_root_uri ); 
  324.  
  325. /** 
  326. * Retrieve theme roots. 
  327. * 
  328. * @since 2.9.0 
  329. * 
  330. * @global array $wp_theme_directories 
  331. * 
  332. * @return array|string An array of theme roots keyed by template/stylesheet or a single theme root if all themes have the same root. 
  333. */ 
  334. function get_theme_roots() { 
  335. global $wp_theme_directories; 
  336.  
  337. if ( count($wp_theme_directories) <= 1 ) 
  338. return '/themes'; 
  339.  
  340. $theme_roots = get_site_transient( 'theme_roots' ); 
  341. if ( false === $theme_roots ) { 
  342. search_theme_directories( true ); // Regenerate the transient. 
  343. $theme_roots = get_site_transient( 'theme_roots' ); 
  344. return $theme_roots; 
  345.  
  346. /** 
  347. * Register a directory that contains themes. 
  348. * 
  349. * @since 2.9.0 
  350. * 
  351. * @global array $wp_theme_directories 
  352. * 
  353. * @param string $directory Either the full filesystem path to a theme folder or a folder within WP_CONTENT_DIR 
  354. * @return bool 
  355. */ 
  356. function register_theme_directory( $directory ) { 
  357. global $wp_theme_directories; 
  358.  
  359. if ( ! file_exists( $directory ) ) { 
  360. // Try prepending as the theme directory could be relative to the content directory 
  361. $directory = WP_CONTENT_DIR . '/' . $directory; 
  362. // If this directory does not exist, return and do not register 
  363. if ( ! file_exists( $directory ) ) { 
  364. return false; 
  365.  
  366. if ( ! is_array( $wp_theme_directories ) ) { 
  367. $wp_theme_directories = array(); 
  368.  
  369. $untrailed = untrailingslashit( $directory ); 
  370. if ( ! empty( $untrailed ) && ! in_array( $untrailed, $wp_theme_directories ) ) { 
  371. $wp_theme_directories[] = $untrailed; 
  372.  
  373. return true; 
  374.  
  375. /** 
  376. * Search all registered theme directories for complete and valid themes. 
  377. * 
  378. * @since 2.9.0 
  379. * 
  380. * @global array $wp_theme_directories 
  381. * @staticvar array $found_themes 
  382. * 
  383. * @param bool $force Optional. Whether to force a new directory scan. Defaults to false. 
  384. * @return array|false Valid themes found 
  385. */ 
  386. function search_theme_directories( $force = false ) { 
  387. global $wp_theme_directories; 
  388. static $found_themes = null; 
  389.  
  390. if ( empty( $wp_theme_directories ) ) 
  391. return false; 
  392.  
  393. if ( ! $force && isset( $found_themes ) ) 
  394. return $found_themes; 
  395.  
  396. $found_themes = array(); 
  397.  
  398. $wp_theme_directories = (array) $wp_theme_directories; 
  399. $relative_theme_roots = array(); 
  400.  
  401. // Set up maybe-relative, maybe-absolute array of theme directories. 
  402. // We always want to return absolute, but we need to cache relative 
  403. // to use in get_theme_root(). 
  404. foreach ( $wp_theme_directories as $theme_root ) { 
  405. if ( 0 === strpos( $theme_root, WP_CONTENT_DIR ) ) 
  406. $relative_theme_roots[ str_replace( WP_CONTENT_DIR, '', $theme_root ) ] = $theme_root; 
  407. else 
  408. $relative_theme_roots[ $theme_root ] = $theme_root; 
  409.  
  410. /** 
  411. * Filters whether to get the cache of the registered theme directories. 
  412. * 
  413. * @since 3.4.0 
  414. * 
  415. * @param bool $cache_expiration Whether to get the cache of the theme directories. Default false. 
  416. * @param string $cache_directory Directory to be searched for the cache. 
  417. */ 
  418. if ( $cache_expiration = apply_filters( 'wp_cache_themes_persistently', false, 'search_theme_directories' ) ) { 
  419. $cached_roots = get_site_transient( 'theme_roots' ); 
  420. if ( is_array( $cached_roots ) ) { 
  421. foreach ( $cached_roots as $theme_dir => $theme_root ) { 
  422. // A cached theme root is no longer around, so skip it. 
  423. if ( ! isset( $relative_theme_roots[ $theme_root ] ) ) 
  424. continue; 
  425. $found_themes[ $theme_dir ] = array( 
  426. 'theme_file' => $theme_dir . '/style.css',  
  427. 'theme_root' => $relative_theme_roots[ $theme_root ], // Convert relative to absolute. 
  428. ); 
  429. return $found_themes; 
  430. if ( ! is_int( $cache_expiration ) ) 
  431. $cache_expiration = 1800; // half hour 
  432. } else { 
  433. $cache_expiration = 1800; // half hour 
  434.  
  435. /** Loop the registered theme directories and extract all themes */ 
  436. foreach ( $wp_theme_directories as $theme_root ) { 
  437.  
  438. // Start with directories in the root of the current theme directory. 
  439. $dirs = @ scandir( $theme_root ); 
  440. if ( ! $dirs ) { 
  441. trigger_error( "$theme_root is not readable", E_USER_NOTICE ); 
  442. continue; 
  443. foreach ( $dirs as $dir ) { 
  444. if ( ! is_dir( $theme_root . '/' . $dir ) || $dir[0] == '.' || $dir == 'CVS' ) 
  445. continue; 
  446. if ( file_exists( $theme_root . '/' . $dir . '/style.css' ) ) { 
  447. // wp-content/themes/a-single-theme 
  448. // wp-content/themes is $theme_root, a-single-theme is $dir 
  449. $found_themes[ $dir ] = array( 
  450. 'theme_file' => $dir . '/style.css',  
  451. 'theme_root' => $theme_root,  
  452. ); 
  453. } else { 
  454. $found_theme = false; 
  455. // wp-content/themes/a-folder-of-themes/* 
  456. // wp-content/themes is $theme_root, a-folder-of-themes is $dir, then themes are $sub_dirs 
  457. $sub_dirs = @ scandir( $theme_root . '/' . $dir ); 
  458. if ( ! $sub_dirs ) { 
  459. trigger_error( "$theme_root/$dir is not readable", E_USER_NOTICE ); 
  460. continue; 
  461. foreach ( $sub_dirs as $sub_dir ) { 
  462. if ( ! is_dir( $theme_root . '/' . $dir . '/' . $sub_dir ) || $dir[0] == '.' || $dir == 'CVS' ) 
  463. continue; 
  464. if ( ! file_exists( $theme_root . '/' . $dir . '/' . $sub_dir . '/style.css' ) ) 
  465. continue; 
  466. $found_themes[ $dir . '/' . $sub_dir ] = array( 
  467. 'theme_file' => $dir . '/' . $sub_dir . '/style.css',  
  468. 'theme_root' => $theme_root,  
  469. ); 
  470. $found_theme = true; 
  471. // Never mind the above, it's just a theme missing a style.css. 
  472. // Return it; WP_Theme will catch the error. 
  473. if ( ! $found_theme ) 
  474. $found_themes[ $dir ] = array( 
  475. 'theme_file' => $dir . '/style.css',  
  476. 'theme_root' => $theme_root,  
  477. ); 
  478.  
  479. asort( $found_themes ); 
  480.  
  481. $theme_roots = array(); 
  482. $relative_theme_roots = array_flip( $relative_theme_roots ); 
  483.  
  484. foreach ( $found_themes as $theme_dir => $theme_data ) { 
  485. $theme_roots[ $theme_dir ] = $relative_theme_roots[ $theme_data['theme_root'] ]; // Convert absolute to relative. 
  486.  
  487. if ( $theme_roots != get_site_transient( 'theme_roots' ) ) 
  488. set_site_transient( 'theme_roots', $theme_roots, $cache_expiration ); 
  489.  
  490. return $found_themes; 
  491.  
  492. /** 
  493. * Retrieve path to themes directory. 
  494. * 
  495. * Does not have trailing slash. 
  496. * 
  497. * @since 1.5.0 
  498. * 
  499. * @global array $wp_theme_directories 
  500. * 
  501. * @param string $stylesheet_or_template The stylesheet or template name of the theme 
  502. * @return string Theme path. 
  503. */ 
  504. function get_theme_root( $stylesheet_or_template = false ) { 
  505. global $wp_theme_directories; 
  506.  
  507. if ( $stylesheet_or_template && $theme_root = get_raw_theme_root( $stylesheet_or_template ) ) { 
  508. // Always prepend WP_CONTENT_DIR unless the root currently registered as a theme directory. 
  509. // This gives relative theme roots the benefit of the doubt when things go haywire. 
  510. if ( ! in_array( $theme_root, (array) $wp_theme_directories ) ) 
  511. $theme_root = WP_CONTENT_DIR . $theme_root; 
  512. } else { 
  513. $theme_root = WP_CONTENT_DIR . '/themes'; 
  514.  
  515. /** 
  516. * Filters the absolute path to the themes directory. 
  517. * 
  518. * @since 1.5.0 
  519. * 
  520. * @param string $theme_root Absolute path to themes directory. 
  521. */ 
  522. return apply_filters( 'theme_root', $theme_root ); 
  523.  
  524. /** 
  525. * Retrieve URI for themes directory. 
  526. * 
  527. * Does not have trailing slash. 
  528. * 
  529. * @since 1.5.0 
  530. * 
  531. * @global array $wp_theme_directories 
  532. * 
  533. * @param string $stylesheet_or_template Optional. The stylesheet or template name of the theme. 
  534. * Default is to leverage the main theme root. 
  535. * @param string $theme_root Optional. The theme root for which calculations will be based, preventing 
  536. * the need for a get_raw_theme_root() call. 
  537. * @return string Themes URI. 
  538. */ 
  539. function get_theme_root_uri( $stylesheet_or_template = false, $theme_root = false ) { 
  540. global $wp_theme_directories; 
  541.  
  542. if ( $stylesheet_or_template && ! $theme_root ) 
  543. $theme_root = get_raw_theme_root( $stylesheet_or_template ); 
  544.  
  545. if ( $stylesheet_or_template && $theme_root ) { 
  546. if ( in_array( $theme_root, (array) $wp_theme_directories ) ) { 
  547. // Absolute path. Make an educated guess. YMMV -- but note the filter below. 
  548. if ( 0 === strpos( $theme_root, WP_CONTENT_DIR ) ) 
  549. $theme_root_uri = content_url( str_replace( WP_CONTENT_DIR, '', $theme_root ) ); 
  550. elseif ( 0 === strpos( $theme_root, ABSPATH ) ) 
  551. $theme_root_uri = site_url( str_replace( ABSPATH, '', $theme_root ) ); 
  552. elseif ( 0 === strpos( $theme_root, WP_PLUGIN_DIR ) || 0 === strpos( $theme_root, WPMU_PLUGIN_DIR ) ) 
  553. $theme_root_uri = plugins_url( basename( $theme_root ), $theme_root ); 
  554. else 
  555. $theme_root_uri = $theme_root; 
  556. } else { 
  557. $theme_root_uri = content_url( $theme_root ); 
  558. } else { 
  559. $theme_root_uri = content_url( 'themes' ); 
  560.  
  561. /** 
  562. * Filters the URI for themes directory. 
  563. * 
  564. * @since 1.5.0 
  565. * 
  566. * @param string $theme_root_uri The URI for themes directory. 
  567. * @param string $siteurl WordPress web address which is set in General Options. 
  568. * @param string $stylesheet_or_template Stylesheet or template name of the theme. 
  569. */ 
  570. return apply_filters( 'theme_root_uri', $theme_root_uri, get_option( 'siteurl' ), $stylesheet_or_template ); 
  571.  
  572. /** 
  573. * Get the raw theme root relative to the content directory with no filters applied. 
  574. * 
  575. * @since 3.1.0 
  576. * 
  577. * @global array $wp_theme_directories 
  578. * 
  579. * @param string $stylesheet_or_template The stylesheet or template name of the theme 
  580. * @param bool $skip_cache Optional. Whether to skip the cache. 
  581. * Defaults to false, meaning the cache is used. 
  582. * @return string Theme root 
  583. */ 
  584. function get_raw_theme_root( $stylesheet_or_template, $skip_cache = false ) { 
  585. global $wp_theme_directories; 
  586.  
  587. if ( count($wp_theme_directories) <= 1 ) 
  588. return '/themes'; 
  589.  
  590. $theme_root = false; 
  591.  
  592. // If requesting the root for the current theme, consult options to avoid calling get_theme_roots() 
  593. if ( ! $skip_cache ) { 
  594. if ( get_option('stylesheet') == $stylesheet_or_template ) 
  595. $theme_root = get_option('stylesheet_root'); 
  596. elseif ( get_option('template') == $stylesheet_or_template ) 
  597. $theme_root = get_option('template_root'); 
  598.  
  599. if ( empty($theme_root) ) { 
  600. $theme_roots = get_theme_roots(); 
  601. if ( !empty($theme_roots[$stylesheet_or_template]) ) 
  602. $theme_root = $theme_roots[$stylesheet_or_template]; 
  603.  
  604. return $theme_root; 
  605.  
  606. /** 
  607. * Display localized stylesheet link element. 
  608. * 
  609. * @since 2.1.0 
  610. */ 
  611. function locale_stylesheet() { 
  612. $stylesheet = get_locale_stylesheet_uri(); 
  613. if ( empty($stylesheet) ) 
  614. return; 
  615. echo '<link rel="stylesheet" href="' . $stylesheet . '" type="text/css" media="screen" />'; 
  616.  
  617. /** 
  618. * Switches the theme. 
  619. * 
  620. * Accepts one argument: $stylesheet of the theme. It also accepts an additional function signature 
  621. * of two arguments: $template then $stylesheet. This is for backward compatibility. 
  622. * 
  623. * @since 2.5.0 
  624. * 
  625. * @global array $wp_theme_directories 
  626. * @global WP_Customize_Manager $wp_customize 
  627. * @global array $sidebars_widgets 
  628. * 
  629. * @param string $stylesheet Stylesheet name 
  630. */ 
  631. function switch_theme( $stylesheet ) { 
  632. global $wp_theme_directories, $wp_customize, $sidebars_widgets; 
  633.  
  634. $_sidebars_widgets = null; 
  635. if ( 'wp_ajax_customize_save' === current_action() ) { 
  636. $_sidebars_widgets = $wp_customize->post_value( $wp_customize->get_setting( 'old_sidebars_widgets_data' ) ); 
  637. } elseif ( is_array( $sidebars_widgets ) ) { 
  638. $_sidebars_widgets = $sidebars_widgets; 
  639.  
  640. if ( is_array( $_sidebars_widgets ) ) { 
  641. set_theme_mod( 'sidebars_widgets', array( 'time' => time(), 'data' => $_sidebars_widgets ) ); 
  642.  
  643. $nav_menu_locations = get_theme_mod( 'nav_menu_locations' ); 
  644.  
  645. if ( func_num_args() > 1 ) { 
  646. $stylesheet = func_get_arg( 1 ); 
  647.  
  648. $old_theme = wp_get_theme(); 
  649. $new_theme = wp_get_theme( $stylesheet ); 
  650. $template = $new_theme->get_template(); 
  651.  
  652. update_option( 'template', $template ); 
  653. update_option( 'stylesheet', $stylesheet ); 
  654.  
  655. if ( count( $wp_theme_directories ) > 1 ) { 
  656. update_option( 'template_root', get_raw_theme_root( $template, true ) ); 
  657. update_option( 'stylesheet_root', get_raw_theme_root( $stylesheet, true ) ); 
  658. } else { 
  659. delete_option( 'template_root' ); 
  660. delete_option( 'stylesheet_root' ); 
  661.  
  662. $new_name = $new_theme->get('Name'); 
  663.  
  664. update_option( 'current_theme', $new_name ); 
  665.  
  666. // Migrate from the old mods_{name} option to theme_mods_{slug}. 
  667. if ( is_admin() && false === get_option( 'theme_mods_' . $stylesheet ) ) { 
  668. $default_theme_mods = (array) get_option( 'mods_' . $new_name ); 
  669. if ( ! empty( $nav_menu_locations ) && empty( $default_theme_mods['nav_menu_locations'] ) ) { 
  670. $default_theme_mods['nav_menu_locations'] = $nav_menu_locations; 
  671. add_option( "theme_mods_$stylesheet", $default_theme_mods ); 
  672. } else { 
  673. /** 
  674. * Since retrieve_widgets() is called when initializing a theme in the Customizer,  
  675. * we need to remove the theme mods to avoid overwriting changes made via 
  676. * the Customizer when accessing wp-admin/widgets.php. 
  677. */ 
  678. if ( 'wp_ajax_customize_save' === current_action() ) { 
  679. remove_theme_mod( 'sidebars_widgets' ); 
  680.  
  681. if ( ! empty( $nav_menu_locations ) ) { 
  682. $nav_mods = get_theme_mod( 'nav_menu_locations' ); 
  683. if ( empty( $nav_mods ) ) { 
  684. set_theme_mod( 'nav_menu_locations', $nav_menu_locations ); 
  685.  
  686. update_option( 'theme_switched', $old_theme->get_stylesheet() ); 
  687.  
  688. /** 
  689. * Fires after the theme is switched. 
  690. * 
  691. * @since 1.5.0 
  692. * @since 4.5.0 Introduced the `$old_theme` parameter. 
  693. * 
  694. * @param string $new_name Name of the new theme. 
  695. * @param WP_Theme $new_theme WP_Theme instance of the new theme. 
  696. * @param WP_Theme $old_theme WP_Theme instance of the old theme. 
  697. */ 
  698. do_action( 'switch_theme', $new_name, $new_theme, $old_theme ); 
  699.  
  700. /** 
  701. * Checks that current theme files 'index.php' and 'style.css' exists. 
  702. * 
  703. * Does not initially check the default theme, which is the fallback and should always exist. 
  704. * But if it doesn't exist, it'll fall back to the latest core default theme that does exist. 
  705. * Will switch theme to the fallback theme if current theme does not validate. 
  706. * 
  707. * You can use the {@see 'validate_current_theme'} filter to return false to 
  708. * disable this functionality. 
  709. * 
  710. * @since 1.5.0 
  711. * @see WP_DEFAULT_THEME 
  712. * 
  713. * @return bool 
  714. */ 
  715. function validate_current_theme() { 
  716. /** 
  717. * Filters whether to validate the current theme. 
  718. * 
  719. * @since 2.7.0 
  720. * 
  721. * @param bool $validate Whether to validate the current theme. Default true. 
  722. */ 
  723. if ( wp_installing() || ! apply_filters( 'validate_current_theme', true ) ) 
  724. return true; 
  725.  
  726. if ( ! file_exists( get_template_directory() . '/index.php' ) ) { 
  727. // Invalid. 
  728. } elseif ( ! file_exists( get_template_directory() . '/style.css' ) ) { 
  729. // Invalid. 
  730. } elseif ( is_child_theme() && ! file_exists( get_stylesheet_directory() . '/style.css' ) ) { 
  731. // Invalid. 
  732. } else { 
  733. // Valid. 
  734. return true; 
  735.  
  736. $default = wp_get_theme( WP_DEFAULT_THEME ); 
  737. if ( $default->exists() ) { 
  738. switch_theme( WP_DEFAULT_THEME ); 
  739. return false; 
  740.  
  741. /** 
  742. * If we're in an invalid state but WP_DEFAULT_THEME doesn't exist,  
  743. * switch to the latest core default theme that's installed. 
  744. * If it turns out that this latest core default theme is our current 
  745. * theme, then there's nothing we can do about that, so we have to bail,  
  746. * rather than going into an infinite loop. (This is why there are 
  747. * checks against WP_DEFAULT_THEME above, also.) We also can't do anything 
  748. * if it turns out there is no default theme installed. (That's `false`.) 
  749. */ 
  750. $default = WP_Theme::get_core_default_theme(); 
  751. if ( false === $default || get_stylesheet() == $default->get_stylesheet() ) { 
  752. return true; 
  753.  
  754. switch_theme( $default->get_stylesheet() ); 
  755. return false; 
  756.  
  757. /** 
  758. * Retrieve all theme modifications. 
  759. * 
  760. * @since 3.1.0 
  761. * 
  762. * @return array|void Theme modifications. 
  763. */ 
  764. function get_theme_mods() { 
  765. $theme_slug = get_option( 'stylesheet' ); 
  766. $mods = get_option( "theme_mods_$theme_slug" ); 
  767. if ( false === $mods ) { 
  768. $theme_name = get_option( 'current_theme' ); 
  769. if ( false === $theme_name ) 
  770. $theme_name = wp_get_theme()->get('Name'); 
  771. $mods = get_option( "mods_$theme_name" ); // Deprecated location. 
  772. if ( is_admin() && false !== $mods ) { 
  773. update_option( "theme_mods_$theme_slug", $mods ); 
  774. delete_option( "mods_$theme_name" ); 
  775. return $mods; 
  776.  
  777. /** 
  778. * Retrieve theme modification value for the current theme. 
  779. * 
  780. * If the modification name does not exist, then the $default will be passed 
  781. * through {@link https://secure.php.net/sprintf sprintf()} PHP function with the first 
  782. * string the template directory URI and the second string the stylesheet 
  783. * directory URI. 
  784. * 
  785. * @since 2.1.0 
  786. * 
  787. * @param string $name Theme modification name. 
  788. * @param bool|string $default 
  789. * @return string 
  790. */ 
  791. function get_theme_mod( $name, $default = false ) { 
  792. $mods = get_theme_mods(); 
  793.  
  794. if ( isset( $mods[$name] ) ) { 
  795. /** 
  796. * Filters the theme modification, or 'theme_mod', value. 
  797. * 
  798. * The dynamic portion of the hook name, `$name`, refers to 
  799. * the key name of the modification array. For example,  
  800. * 'header_textcolor', 'header_image', and so on depending 
  801. * on the theme options. 
  802. * 
  803. * @since 2.2.0 
  804. * 
  805. * @param string $current_mod The value of the current theme modification. 
  806. */ 
  807. return apply_filters( "theme_mod_{$name}", $mods[$name] ); 
  808.  
  809. if ( is_string( $default ) ) 
  810. $default = sprintf( $default, get_template_directory_uri(), get_stylesheet_directory_uri() ); 
  811.  
  812. /** This filter is documented in wp-includes/theme.php */ 
  813. return apply_filters( "theme_mod_{$name}", $default ); 
  814.  
  815. /** 
  816. * Update theme modification value for the current theme. 
  817. * 
  818. * @since 2.1.0 
  819. * 
  820. * @param string $name Theme modification name. 
  821. * @param mixed $value Theme modification value. 
  822. */ 
  823. function set_theme_mod( $name, $value ) { 
  824. $mods = get_theme_mods(); 
  825. $old_value = isset( $mods[ $name ] ) ? $mods[ $name ] : false; 
  826.  
  827. /** 
  828. * Filters the theme mod value on save. 
  829. * 
  830. * The dynamic portion of the hook name, `$name`, refers to the key name of 
  831. * the modification array. For example, 'header_textcolor', 'header_image',  
  832. * and so on depending on the theme options. 
  833. * 
  834. * @since 3.9.0 
  835. * 
  836. * @param string $value The new value of the theme mod. 
  837. * @param string $old_value The current value of the theme mod. 
  838. */ 
  839. $mods[ $name ] = apply_filters( "pre_set_theme_mod_{$name}", $value, $old_value ); 
  840.  
  841. $theme = get_option( 'stylesheet' ); 
  842. update_option( "theme_mods_$theme", $mods ); 
  843.  
  844. /** 
  845. * Remove theme modification name from current theme list. 
  846. * 
  847. * If removing the name also removes all elements, then the entire option will 
  848. * be removed. 
  849. * 
  850. * @since 2.1.0 
  851. * 
  852. * @param string $name Theme modification name. 
  853. */ 
  854. function remove_theme_mod( $name ) { 
  855. $mods = get_theme_mods(); 
  856.  
  857. if ( ! isset( $mods[ $name ] ) ) 
  858. return; 
  859.  
  860. unset( $mods[ $name ] ); 
  861.  
  862. if ( empty( $mods ) ) { 
  863. remove_theme_mods(); 
  864. return; 
  865. $theme = get_option( 'stylesheet' ); 
  866. update_option( "theme_mods_$theme", $mods ); 
  867.  
  868. /** 
  869. * Remove theme modifications option for current theme. 
  870. * 
  871. * @since 2.1.0 
  872. */ 
  873. function remove_theme_mods() { 
  874. delete_option( 'theme_mods_' . get_option( 'stylesheet' ) ); 
  875.  
  876. // Old style. 
  877. $theme_name = get_option( 'current_theme' ); 
  878. if ( false === $theme_name ) 
  879. $theme_name = wp_get_theme()->get('Name'); 
  880. delete_option( 'mods_' . $theme_name ); 
  881.  
  882. /** 
  883. * Retrieves the custom header text color in HEX format. 
  884. * 
  885. * @since 2.1.0 
  886. * 
  887. * @return string Header text color in HEX format (minus the hash symbol). 
  888. */ 
  889. function get_header_textcolor() { 
  890. return get_theme_mod('header_textcolor', get_theme_support( 'custom-header', 'default-text-color' ) ); 
  891.  
  892. /** 
  893. * Displays the custom header text color in HEX format (minus the hash symbol). 
  894. * 
  895. * @since 2.1.0 
  896. */ 
  897. function header_textcolor() { 
  898. echo get_header_textcolor(); 
  899.  
  900. /** 
  901. * Whether to display the header text. 
  902. * 
  903. * @since 3.4.0 
  904. * 
  905. * @return bool 
  906. */ 
  907. function display_header_text() { 
  908. if ( ! current_theme_supports( 'custom-header', 'header-text' ) ) 
  909. return false; 
  910.  
  911. $text_color = get_theme_mod( 'header_textcolor', get_theme_support( 'custom-header', 'default-text-color' ) ); 
  912. return 'blank' !== $text_color; 
  913.  
  914. /** 
  915. * Check whether a header image is set or not. 
  916. * 
  917. * @since 4.2.0 
  918. * 
  919. * @see get_header_image() 
  920. * 
  921. * @return bool Whether a header image is set or not. 
  922. */ 
  923. function has_header_image() { 
  924. return (bool) get_header_image(); 
  925.  
  926. /** 
  927. * Retrieve header image for custom header. 
  928. * 
  929. * @since 2.1.0 
  930. * 
  931. * @return string|false 
  932. */ 
  933. function get_header_image() { 
  934. $url = get_theme_mod( 'header_image', get_theme_support( 'custom-header', 'default-image' ) ); 
  935.  
  936. if ( 'remove-header' == $url ) 
  937. return false; 
  938.  
  939. if ( is_random_header_image() ) 
  940. $url = get_random_header_image(); 
  941.  
  942. return esc_url_raw( set_url_scheme( $url ) ); 
  943.  
  944. /** 
  945. * Create image tag markup for a custom header image. 
  946. * 
  947. * @since 4.4.0 
  948. * 
  949. * @param array $attr Optional. Additional attributes for the image tag. Can be used 
  950. * to override the default attributes. Default empty. 
  951. * @return string HTML image element markup or empty string on failure. 
  952. */ 
  953. function get_header_image_tag( $attr = array() ) { 
  954. $header = get_custom_header(); 
  955. $header->url = get_header_image(); 
  956.  
  957. if ( ! $header->url ) { 
  958. return ''; 
  959.  
  960. $width = absint( $header->width ); 
  961. $height = absint( $header->height ); 
  962.  
  963. $attr = wp_parse_args( 
  964. $attr,  
  965. array( 
  966. 'src' => $header->url,  
  967. 'width' => $width,  
  968. 'height' => $height,  
  969. 'alt' => get_bloginfo( 'name' ),  
  970. ); 
  971.  
  972. // Generate 'srcset' and 'sizes' if not already present. 
  973. if ( empty( $attr['srcset'] ) && ! empty( $header->attachment_id ) ) { 
  974. $image_meta = get_post_meta( $header->attachment_id, '_wp_attachment_metadata', true ); 
  975. $size_array = array( $width, $height ); 
  976.  
  977. if ( is_array( $image_meta ) ) { 
  978. $srcset = wp_calculate_image_srcset( $size_array, $header->url, $image_meta, $header->attachment_id ); 
  979. $sizes = ! empty( $attr['sizes'] ) ? $attr['sizes'] : wp_calculate_image_sizes( $size_array, $header->url, $image_meta, $header->attachment_id ); 
  980.  
  981. if ( $srcset && $sizes ) { 
  982. $attr['srcset'] = $srcset; 
  983. $attr['sizes'] = $sizes; 
  984.  
  985. $attr = array_map( 'esc_attr', $attr ); 
  986. $html = '<img'; 
  987.  
  988. foreach ( $attr as $name => $value ) { 
  989. $html .= ' ' . $name . '="' . $value . '"'; 
  990.  
  991. $html .= ' />'; 
  992.  
  993. /** 
  994. * Filters the markup of header images. 
  995. * 
  996. * @since 4.4.0 
  997. * 
  998. * @param string $html The HTML image tag markup being filtered. 
  999. * @param object $header The custom header object returned by 'get_custom_header()'. 
  1000. * @param array $attr Array of the attributes for the image tag. 
  1001. */ 
  1002. return apply_filters( 'get_header_image_tag', $html, $header, $attr ); 
  1003.  
  1004. /** 
  1005. * Display the image markup for a custom header image. 
  1006. * 
  1007. * @since 4.4.0 
  1008. * 
  1009. * @param array $attr Optional. Attributes for the image markup. Default empty. 
  1010. */ 
  1011. function the_header_image_tag( $attr = array() ) { 
  1012. echo get_header_image_tag( $attr ); 
  1013.  
  1014. /** 
  1015. * Get random header image data from registered images in theme. 
  1016. * 
  1017. * @since 3.4.0 
  1018. * 
  1019. * @access private 
  1020. * 
  1021. * @global array $_wp_default_headers 
  1022. * @staticvar object $_wp_random_header 
  1023. * 
  1024. * @return object 
  1025. */ 
  1026. function _get_random_header_data() { 
  1027. static $_wp_random_header = null; 
  1028.  
  1029. if ( empty( $_wp_random_header ) ) { 
  1030. global $_wp_default_headers; 
  1031. $header_image_mod = get_theme_mod( 'header_image', '' ); 
  1032. $headers = array(); 
  1033.  
  1034. if ( 'random-uploaded-image' == $header_image_mod ) 
  1035. $headers = get_uploaded_header_images(); 
  1036. elseif ( ! empty( $_wp_default_headers ) ) { 
  1037. if ( 'random-default-image' == $header_image_mod ) { 
  1038. $headers = $_wp_default_headers; 
  1039. } else { 
  1040. if ( current_theme_supports( 'custom-header', 'random-default' ) ) 
  1041. $headers = $_wp_default_headers; 
  1042.  
  1043. if ( empty( $headers ) ) 
  1044. return new stdClass; 
  1045.  
  1046. $_wp_random_header = (object) $headers[ array_rand( $headers ) ]; 
  1047.  
  1048. $_wp_random_header->url = sprintf( $_wp_random_header->url, get_template_directory_uri(), get_stylesheet_directory_uri() ); 
  1049. $_wp_random_header->thumbnail_url = sprintf( $_wp_random_header->thumbnail_url, get_template_directory_uri(), get_stylesheet_directory_uri() ); 
  1050. return $_wp_random_header; 
  1051.  
  1052. /** 
  1053. * Get random header image url from registered images in theme. 
  1054. * 
  1055. * @since 3.2.0 
  1056. * 
  1057. * @return string Path to header image 
  1058. */ 
  1059. function get_random_header_image() { 
  1060. $random_image = _get_random_header_data(); 
  1061. if ( empty( $random_image->url ) ) 
  1062. return ''; 
  1063. return $random_image->url; 
  1064.  
  1065. /** 
  1066. * Check if random header image is in use. 
  1067. * 
  1068. * Always true if user expressly chooses the option in Appearance > Header. 
  1069. * Also true if theme has multiple header images registered, no specific header image 
  1070. * is chosen, and theme turns on random headers with add_theme_support(). 
  1071. * 
  1072. * @since 3.2.0 
  1073. * 
  1074. * @param string $type The random pool to use. any|default|uploaded 
  1075. * @return bool 
  1076. */ 
  1077. function is_random_header_image( $type = 'any' ) { 
  1078. $header_image_mod = get_theme_mod( 'header_image', get_theme_support( 'custom-header', 'default-image' ) ); 
  1079.  
  1080. if ( 'any' == $type ) { 
  1081. if ( 'random-default-image' == $header_image_mod || 'random-uploaded-image' == $header_image_mod || ( '' != get_random_header_image() && empty( $header_image_mod ) ) ) 
  1082. return true; 
  1083. } else { 
  1084. if ( "random-$type-image" == $header_image_mod ) 
  1085. return true; 
  1086. elseif ( 'default' == $type && empty( $header_image_mod ) && '' != get_random_header_image() ) 
  1087. return true; 
  1088.  
  1089. return false; 
  1090.  
  1091. /** 
  1092. * Display header image URL. 
  1093. * 
  1094. * @since 2.1.0 
  1095. */ 
  1096. function header_image() { 
  1097. $image = get_header_image(); 
  1098. if ( $image ) { 
  1099. echo esc_url( $image ); 
  1100.  
  1101. /** 
  1102. * Get the header images uploaded for the current theme. 
  1103. * 
  1104. * @since 3.2.0 
  1105. * 
  1106. * @return array 
  1107. */ 
  1108. function get_uploaded_header_images() { 
  1109. $header_images = array(); 
  1110.  
  1111. // @todo caching 
  1112. $headers = get_posts( array( 'post_type' => 'attachment', 'meta_key' => '_wp_attachment_is_custom_header', 'meta_value' => get_option('stylesheet'), 'orderby' => 'none', 'nopaging' => true ) ); 
  1113.  
  1114. if ( empty( $headers ) ) 
  1115. return array(); 
  1116.  
  1117. foreach ( (array) $headers as $header ) { 
  1118. $url = esc_url_raw( wp_get_attachment_url( $header->ID ) ); 
  1119. $header_data = wp_get_attachment_metadata( $header->ID ); 
  1120. $header_index = $header->ID; 
  1121.  
  1122. $header_images[$header_index] = array(); 
  1123. $header_images[$header_index]['attachment_id'] = $header->ID; 
  1124. $header_images[$header_index]['url'] = $url; 
  1125. $header_images[$header_index]['thumbnail_url'] = $url; 
  1126. $header_images[$header_index]['alt_text'] = get_post_meta( $header->ID, '_wp_attachment_image_alt', true ); 
  1127.  
  1128. if ( isset( $header_data['width'] ) ) 
  1129. $header_images[$header_index]['width'] = $header_data['width']; 
  1130. if ( isset( $header_data['height'] ) ) 
  1131. $header_images[$header_index]['height'] = $header_data['height']; 
  1132.  
  1133. return $header_images; 
  1134.  
  1135. /** 
  1136. * Get the header image data. 
  1137. * 
  1138. * @since 3.4.0 
  1139. * 
  1140. * @global array $_wp_default_headers 
  1141. * 
  1142. * @return object 
  1143. */ 
  1144. function get_custom_header() { 
  1145. global $_wp_default_headers; 
  1146.  
  1147. if ( is_random_header_image() ) { 
  1148. $data = _get_random_header_data(); 
  1149. } else { 
  1150. $data = get_theme_mod( 'header_image_data' ); 
  1151. if ( ! $data && current_theme_supports( 'custom-header', 'default-image' ) ) { 
  1152. $directory_args = array( get_template_directory_uri(), get_stylesheet_directory_uri() ); 
  1153. $data = array(); 
  1154. $data['url'] = $data['thumbnail_url'] = vsprintf( get_theme_support( 'custom-header', 'default-image' ), $directory_args ); 
  1155. if ( ! empty( $_wp_default_headers ) ) { 
  1156. foreach ( (array) $_wp_default_headers as $default_header ) { 
  1157. $url = vsprintf( $default_header['url'], $directory_args ); 
  1158. if ( $data['url'] == $url ) { 
  1159. $data = $default_header; 
  1160. $data['url'] = $url; 
  1161. $data['thumbnail_url'] = vsprintf( $data['thumbnail_url'], $directory_args ); 
  1162. break; 
  1163.  
  1164. $default = array( 
  1165. 'url' => '',  
  1166. 'thumbnail_url' => '',  
  1167. 'width' => get_theme_support( 'custom-header', 'width' ),  
  1168. 'height' => get_theme_support( 'custom-header', 'height' ),  
  1169. 'video' => get_theme_support( 'custom-header', 'video' ),  
  1170. ); 
  1171. return (object) wp_parse_args( $data, $default ); 
  1172.  
  1173. /** 
  1174. * Register a selection of default headers to be displayed by the custom header admin UI. 
  1175. * 
  1176. * @since 3.0.0 
  1177. * 
  1178. * @global array $_wp_default_headers 
  1179. * 
  1180. * @param array $headers Array of headers keyed by a string id. The ids point to arrays containing 'url', 'thumbnail_url', and 'description' keys. 
  1181. */ 
  1182. function register_default_headers( $headers ) { 
  1183. global $_wp_default_headers; 
  1184.  
  1185. $_wp_default_headers = array_merge( (array) $_wp_default_headers, (array) $headers ); 
  1186.  
  1187. /** 
  1188. * Unregister default headers. 
  1189. * 
  1190. * This function must be called after register_default_headers() has already added the 
  1191. * header you want to remove. 
  1192. * 
  1193. * @see register_default_headers() 
  1194. * @since 3.0.0 
  1195. * 
  1196. * @global array $_wp_default_headers 
  1197. * 
  1198. * @param string|array $header The header string id (key of array) to remove, or an array thereof. 
  1199. * @return bool|void A single header returns true on success, false on failure. 
  1200. * There is currently no return value for multiple headers. 
  1201. */ 
  1202. function unregister_default_headers( $header ) { 
  1203. global $_wp_default_headers; 
  1204. if ( is_array( $header ) ) { 
  1205. array_map( 'unregister_default_headers', $header ); 
  1206. } elseif ( isset( $_wp_default_headers[ $header ] ) ) { 
  1207. unset( $_wp_default_headers[ $header ] ); 
  1208. return true; 
  1209. } else { 
  1210. return false; 
  1211.  
  1212. /** 
  1213. * Check whether a header video is set or not. 
  1214. * 
  1215. * @since 4.7.0 
  1216. * 
  1217. * @see get_header_video_url() 
  1218. * 
  1219. * @return bool Whether a header video is set or not. 
  1220. */ 
  1221. function has_header_video() { 
  1222. return (bool) get_header_video_url(); 
  1223.  
  1224. /** Retrieve header video URL for custom header. 
  1225. * 
  1226. * Uses a local video if present, or falls back to an external video. Returns false if there is no video. 
  1227. * 
  1228. * @since 4.7.0 
  1229. * 
  1230. * @return string|false 
  1231. */ 
  1232. function get_header_video_url() { 
  1233. $id = absint( get_theme_mod( 'header_video' ) ); 
  1234. $url = esc_url( get_theme_mod( 'external_header_video' ) ); 
  1235.  
  1236. if ( $id ) { 
  1237. // Get the file URL from the attachment ID. 
  1238. $url = wp_get_attachment_url( $id ); 
  1239.  
  1240. /** 
  1241. * Filters the header video URL. 
  1242. * 
  1243. * @since 4.7.3 
  1244. * 
  1245. * @param string $url Header video URL, if available. 
  1246. */ 
  1247. $url = apply_filters( 'get_header_video_url', $url ); 
  1248.  
  1249. if ( ! $id && ! $url ) { 
  1250. return false; 
  1251.  
  1252. return esc_url_raw( set_url_scheme( $url ) ); 
  1253.  
  1254. /** 
  1255. * Display header video URL. 
  1256. * 
  1257. * @since 4.7.0 
  1258. */ 
  1259. function the_header_video_url() { 
  1260. $video = get_header_video_url(); 
  1261. if ( $video ) { 
  1262. echo esc_url( $video ); 
  1263.  
  1264. /** 
  1265. * Retrieve header video settings. 
  1266. * 
  1267. * @since 4.7.0 
  1268. * 
  1269. * @return array 
  1270. */ 
  1271. function get_header_video_settings() { 
  1272. $header = get_custom_header(); 
  1273. $video_url = get_header_video_url(); 
  1274. $video_type = wp_check_filetype( $video_url, wp_get_mime_types() ); 
  1275.  
  1276. $settings = array( 
  1277. 'mimeType' => '',  
  1278. 'posterUrl' => get_header_image(),  
  1279. 'videoUrl' => $video_url,  
  1280. 'width' => absint( $header->width ),  
  1281. 'height' => absint( $header->height ),  
  1282. 'minWidth' => 900,  
  1283. 'minHeight' => 500,  
  1284. 'l10n' => array( 
  1285. 'pause' => __( 'Pause' ),  
  1286. 'play' => __( 'Play' ),  
  1287. 'pauseSpeak' => __( 'Video is paused.'),  
  1288. 'playSpeak' => __( 'Video is playing.'),  
  1289. ),  
  1290. ); 
  1291.  
  1292. if ( preg_match( '#^https?://(?:www\.)?(?:youtube\.com/watch|youtu\.be/)#', $video_url ) ) { 
  1293. $settings['mimeType'] = 'video/x-youtube'; 
  1294. } elseif ( ! empty( $video_type['type'] ) ) { 
  1295. $settings['mimeType'] = $video_type['type']; 
  1296.  
  1297. return apply_filters( 'header_video_settings', $settings ); 
  1298.  
  1299. /** 
  1300. * Check whether a custom header is set or not. 
  1301. * 
  1302. * @since 4.7.0 
  1303. * 
  1304. * @return bool True if a custom header is set. False if not. 
  1305. */ 
  1306. function has_custom_header() { 
  1307. if ( has_header_image() || ( has_header_video() && is_header_video_active() ) ) { 
  1308. return true; 
  1309.  
  1310. return false; 
  1311.  
  1312. /** 
  1313. * Checks whether the custom header video is eligible to show on the current page. 
  1314. * 
  1315. * @since 4.7.0 
  1316. * 
  1317. * @return bool True if the custom header video should be shown. False if not. 
  1318. */ 
  1319. function is_header_video_active() { 
  1320. if ( ! get_theme_support( 'custom-header', 'video' ) ) { 
  1321. return false; 
  1322.  
  1323. $video_active_cb = get_theme_support( 'custom-header', 'video-active-callback' ); 
  1324.  
  1325. if ( empty( $video_active_cb ) || ! is_callable( $video_active_cb ) ) { 
  1326. $show_video = true; 
  1327. } else { 
  1328. $show_video = call_user_func( $video_active_cb ); 
  1329.  
  1330. /** 
  1331. * Modify whether the custom header video is eligible to show on the current page. 
  1332. * 
  1333. * @since 4.7.0 
  1334. * 
  1335. * @param bool $show_video Whether the custom header video should be shown. Returns the value 
  1336. * of the theme setting for the `custom-header`'s `video-active-callback`. 
  1337. * If no callback is set, the default value is that of `is_front_page()`. 
  1338. */ 
  1339. return apply_filters( 'is_header_video_active', $show_video ); 
  1340.  
  1341. /** 
  1342. * Retrieve the markup for a custom header. 
  1343. * 
  1344. * The container div will always be returned in the Customizer preview. 
  1345. * 
  1346. * @since 4.7.0 
  1347. * 
  1348. * @return string The markup for a custom header on success. 
  1349. */ 
  1350. function get_custom_header_markup() { 
  1351. if ( ! has_custom_header() && ! is_customize_preview() ) { 
  1352. return ''; 
  1353.  
  1354. return sprintf( 
  1355. '<div id="wp-custom-header" class="wp-custom-header">%s</div>',  
  1356. get_header_image_tag() 
  1357. ); 
  1358.  
  1359. /** 
  1360. * Print the markup for a custom header. 
  1361. * 
  1362. * A container div will always be printed in the Customizer preview. 
  1363. * 
  1364. * @since 4.7.0 
  1365. */ 
  1366. function the_custom_header_markup() { 
  1367. $custom_header = get_custom_header_markup(); 
  1368. if ( empty( $custom_header ) ) { 
  1369. return; 
  1370.  
  1371. echo $custom_header; 
  1372.  
  1373. if ( is_header_video_active() && ( has_header_video() || is_customize_preview() ) ) { 
  1374. wp_enqueue_script( 'wp-custom-header' ); 
  1375. wp_localize_script( 'wp-custom-header', '_wpCustomHeaderSettings', get_header_video_settings() ); 
  1376.  
  1377. /** 
  1378. * Retrieve background image for custom background. 
  1379. * 
  1380. * @since 3.0.0 
  1381. * 
  1382. * @return string 
  1383. */ 
  1384. function get_background_image() { 
  1385. return get_theme_mod('background_image', get_theme_support( 'custom-background', 'default-image' ) ); 
  1386.  
  1387. /** 
  1388. * Display background image path. 
  1389. * 
  1390. * @since 3.0.0 
  1391. */ 
  1392. function background_image() { 
  1393. echo get_background_image(); 
  1394.  
  1395. /** 
  1396. * Retrieve value for custom background color. 
  1397. * 
  1398. * @since 3.0.0 
  1399. * 
  1400. * @return string 
  1401. */ 
  1402. function get_background_color() { 
  1403. return get_theme_mod('background_color', get_theme_support( 'custom-background', 'default-color' ) ); 
  1404.  
  1405. /** 
  1406. * Display background color value. 
  1407. * 
  1408. * @since 3.0.0 
  1409. */ 
  1410. function background_color() { 
  1411. echo get_background_color(); 
  1412.  
  1413. /** 
  1414. * Default custom background callback. 
  1415. * 
  1416. * @since 3.0.0 
  1417. * @access protected 
  1418. */ 
  1419. function _custom_background_cb() { 
  1420. // $background is the saved custom image, or the default image. 
  1421. $background = set_url_scheme( get_background_image() ); 
  1422.  
  1423. // $color is the saved custom color. 
  1424. // A default has to be specified in style.css. It will not be printed here. 
  1425. $color = get_background_color(); 
  1426.  
  1427. if ( $color === get_theme_support( 'custom-background', 'default-color' ) ) { 
  1428. $color = false; 
  1429.  
  1430. if ( ! $background && ! $color ) { 
  1431. if ( is_customize_preview() ) { 
  1432. echo '<style type="text/css" id="custom-background-css"></style>'; 
  1433. return; 
  1434.  
  1435. $style = $color ? "background-color: #$color;" : ''; 
  1436.  
  1437. if ( $background ) { 
  1438. $image = ' background-image: url("' . esc_url_raw( $background ) . '");'; 
  1439.  
  1440. // Background Position. 
  1441. $position_x = get_theme_mod( 'background_position_x', get_theme_support( 'custom-background', 'default-position-x' ) ); 
  1442. $position_y = get_theme_mod( 'background_position_y', get_theme_support( 'custom-background', 'default-position-y' ) ); 
  1443.  
  1444. if ( ! in_array( $position_x, array( 'left', 'center', 'right' ), true ) ) { 
  1445. $position_x = 'left'; 
  1446.  
  1447. if ( ! in_array( $position_y, array( 'top', 'center', 'bottom' ), true ) ) { 
  1448. $position_y = 'top'; 
  1449.  
  1450. $position = " background-position: $position_x $position_y;"; 
  1451.  
  1452. // Background Size. 
  1453. $size = get_theme_mod( 'background_size', get_theme_support( 'custom-background', 'default-size' ) ); 
  1454.  
  1455. if ( ! in_array( $size, array( 'auto', 'contain', 'cover' ), true ) ) { 
  1456. $size = 'auto'; 
  1457.  
  1458. $size = " background-size: $size;"; 
  1459.  
  1460. // Background Repeat. 
  1461. $repeat = get_theme_mod( 'background_repeat', get_theme_support( 'custom-background', 'default-repeat' ) ); 
  1462.  
  1463. if ( ! in_array( $repeat, array( 'repeat-x', 'repeat-y', 'repeat', 'no-repeat' ), true ) ) { 
  1464. $repeat = 'repeat'; 
  1465.  
  1466. $repeat = " background-repeat: $repeat;"; 
  1467.  
  1468. // Background Scroll. 
  1469. $attachment = get_theme_mod( 'background_attachment', get_theme_support( 'custom-background', 'default-attachment' ) ); 
  1470.  
  1471. if ( 'fixed' !== $attachment ) { 
  1472. $attachment = 'scroll'; 
  1473.  
  1474. $attachment = " background-attachment: $attachment;"; 
  1475.  
  1476. $style .= $image . $position . $size . $repeat . $attachment; 
  1477. ?> 
  1478. <style type="text/css" id="custom-background-css"> 
  1479. body.custom-background { <?php echo trim( $style ); ?> } 
  1480. </style> 
  1481. <?php 
  1482.  
  1483. /** 
  1484. * Render the Custom CSS style element. 
  1485. * 
  1486. * @since 4.7.0 
  1487. * @access public 
  1488. */ 
  1489. function wp_custom_css_cb() { 
  1490. $styles = wp_get_custom_css(); 
  1491. if ( $styles || is_customize_preview() ) : ?> 
  1492. <style type="text/css" id="wp-custom-css"> 
  1493. <?php echo strip_tags( $styles ); // Note that esc_html() cannot be used because `div > span` is not interpreted properly. ?> 
  1494. </style> 
  1495. <?php endif; 
  1496.  
  1497. /** 
  1498. * Fetch the `custom_css` post for a given theme. 
  1499. * 
  1500. * @since 4.7.0 
  1501. * @access public 
  1502. * 
  1503. * @param string $stylesheet Optional. A theme object stylesheet name. Defaults to the current theme. 
  1504. * @return WP_Post|null The custom_css post or null if none exists. 
  1505. */ 
  1506. function wp_get_custom_css_post( $stylesheet = '' ) { 
  1507. if ( empty( $stylesheet ) ) { 
  1508. $stylesheet = get_stylesheet(); 
  1509.  
  1510. $custom_css_query_vars = array( 
  1511. 'post_type' => 'custom_css',  
  1512. 'post_status' => get_post_stati(),  
  1513. 'name' => sanitize_title( $stylesheet ),  
  1514. 'posts_per_page' => 1,  
  1515. 'no_found_rows' => true,  
  1516. 'cache_results' => true,  
  1517. 'update_post_meta_cache' => false,  
  1518. 'update_post_term_cache' => false,  
  1519. 'lazy_load_term_meta' => false,  
  1520. ); 
  1521.  
  1522. $post = null; 
  1523. if ( get_stylesheet() === $stylesheet ) { 
  1524. $post_id = get_theme_mod( 'custom_css_post_id' ); 
  1525.  
  1526. if ( $post_id > 0 && get_post( $post_id ) ) { 
  1527. $post = get_post( $post_id ); 
  1528.  
  1529. // `-1` indicates no post exists; no query necessary. 
  1530. if ( ! $post && -1 !== $post_id ) { 
  1531. $query = new WP_Query( $custom_css_query_vars ); 
  1532. $post = $query->post; 
  1533. /** 
  1534. * Cache the lookup. See wp_update_custom_css_post(). 
  1535. * @todo This should get cleared if a custom_css post is added/removed. 
  1536. */ 
  1537. set_theme_mod( 'custom_css_post_id', $post ? $post->ID : -1 ); 
  1538. } else { 
  1539. $query = new WP_Query( $custom_css_query_vars ); 
  1540. $post = $query->post; 
  1541.  
  1542. return $post; 
  1543.  
  1544. /** 
  1545. * Fetch the saved Custom CSS content for rendering. 
  1546. * 
  1547. * @since 4.7.0 
  1548. * @access public 
  1549. * 
  1550. * @param string $stylesheet Optional. A theme object stylesheet name. Defaults to the current theme. 
  1551. * @return string The Custom CSS Post content. 
  1552. */ 
  1553. function wp_get_custom_css( $stylesheet = '' ) { 
  1554. $css = ''; 
  1555.  
  1556. if ( empty( $stylesheet ) ) { 
  1557. $stylesheet = get_stylesheet(); 
  1558.  
  1559. $post = wp_get_custom_css_post( $stylesheet ); 
  1560. if ( $post ) { 
  1561. $css = $post->post_content; 
  1562.  
  1563. /** 
  1564. * Modify the Custom CSS Output into the <head>. 
  1565. * 
  1566. * @since 4.7.0 
  1567. * 
  1568. * @param string $css CSS pulled in from the Custom CSS CPT. 
  1569. * @param string $stylesheet The theme stylesheet name. 
  1570. */ 
  1571. $css = apply_filters( 'wp_get_custom_css', $css, $stylesheet ); 
  1572.  
  1573. return $css; 
  1574.  
  1575. /** 
  1576. * Update the `custom_css` post for a given theme. 
  1577. * 
  1578. * Inserts a `custom_css` post when one doesn't yet exist. 
  1579. * 
  1580. * @since 4.7.0 
  1581. * @access public 
  1582. * 
  1583. * @param string $css CSS, stored in `post_content`. 
  1584. * @param array $args { 
  1585. * Args. 
  1586. * 
  1587. * @type string $preprocessed Pre-processed CSS, stored in `post_content_filtered`. Normally empty string. Optional. 
  1588. * @type string $stylesheet Stylesheet (child theme) to update. Optional, defaults to current theme/stylesheet. 
  1589. * } 
  1590. * @return WP_Post|WP_Error Post on success, error on failure. 
  1591. */ 
  1592. function wp_update_custom_css_post( $css, $args = array() ) { 
  1593. $args = wp_parse_args( $args, array( 
  1594. 'preprocessed' => '',  
  1595. 'stylesheet' => get_stylesheet(),  
  1596. ) ); 
  1597.  
  1598. $data = array( 
  1599. 'css' => $css,  
  1600. 'preprocessed' => $args['preprocessed'],  
  1601. ); 
  1602.  
  1603. /** 
  1604. * Filters the `css` (`post_content`) and `preprocessed` (`post_content_filtered`) args for a `custom_css` post being updated. 
  1605. * 
  1606. * This filter can be used by plugin that offer CSS pre-processors, to store the original 
  1607. * pre-processed CSS in `post_content_filtered` and then store processed CSS in `post_content`. 
  1608. * When used in this way, the `post_content_filtered` should be supplied as the setting value 
  1609. * instead of `post_content` via a the `customize_value_custom_css` filter, for example: 
  1610. * 
  1611. * <code> 
  1612. * add_filter( 'customize_value_custom_css', function( $value, $setting ) { 
  1613. * $post = wp_get_custom_css_post( $setting->stylesheet ); 
  1614. * if ( $post && ! empty( $post->post_content_filtered ) ) { 
  1615. * $css = $post->post_content_filtered; 
  1616. * } 
  1617. * return $css; 
  1618. * }, 10, 2 ); 
  1619. * </code> 
  1620. * 
  1621. * @since 4.7.0 
  1622. * @param array $data { 
  1623. * Custom CSS data. 
  1624. * 
  1625. * @type string $css CSS stored in `post_content`. 
  1626. * @type string $preprocessed Pre-processed CSS stored in `post_content_filtered`. Normally empty string. 
  1627. * } 
  1628. * @param array $args { 
  1629. * The args passed into `wp_update_custom_css_post()` merged with defaults. 
  1630. * 
  1631. * @type string $css The original CSS passed in to be updated. 
  1632. * @type string $preprocessed The original preprocessed CSS passed in to be updated. 
  1633. * @type string $stylesheet The stylesheet (theme) being updated. 
  1634. * } 
  1635. */ 
  1636. $data = apply_filters( 'update_custom_css_data', $data, array_merge( $args, compact( 'css' ) ) ); 
  1637.  
  1638. $post_data = array( 
  1639. 'post_title' => $args['stylesheet'],  
  1640. 'post_name' => sanitize_title( $args['stylesheet'] ),  
  1641. 'post_type' => 'custom_css',  
  1642. 'post_status' => 'publish',  
  1643. 'post_content' => $data['css'],  
  1644. 'post_content_filtered' => $data['preprocessed'],  
  1645. ); 
  1646.  
  1647. // Update post if it already exists, otherwise create a new one. 
  1648. $post = wp_get_custom_css_post( $args['stylesheet'] ); 
  1649. if ( $post ) { 
  1650. $post_data['ID'] = $post->ID; 
  1651. $r = wp_update_post( wp_slash( $post_data ), true ); 
  1652. } else { 
  1653. $r = wp_insert_post( wp_slash( $post_data ), true ); 
  1654.  
  1655. if ( ! is_wp_error( $r ) ) { 
  1656. if ( get_stylesheet() === $args['stylesheet'] ) { 
  1657. set_theme_mod( 'custom_css_post_id', $r ); 
  1658.  
  1659. // Trigger creation of a revision. This should be removed once #30854 is resolved. 
  1660. if ( 0 === count( wp_get_post_revisions( $r ) ) ) { 
  1661. wp_save_post_revision( $r ); 
  1662.  
  1663. if ( is_wp_error( $r ) ) { 
  1664. return $r; 
  1665. return get_post( $r ); 
  1666.  
  1667. /** 
  1668. * Add callback for custom TinyMCE editor stylesheets. 
  1669. * 
  1670. * The parameter $stylesheet is the name of the stylesheet, relative to 
  1671. * the theme root. It also accepts an array of stylesheets. 
  1672. * It is optional and defaults to 'editor-style.css'. 
  1673. * 
  1674. * This function automatically adds another stylesheet with -rtl prefix, e.g. editor-style-rtl.css. 
  1675. * If that file doesn't exist, it is removed before adding the stylesheet(s) to TinyMCE. 
  1676. * If an array of stylesheets is passed to add_editor_style(),  
  1677. * RTL is only added for the first stylesheet. 
  1678. * 
  1679. * Since version 3.4 the TinyMCE body has .rtl CSS class. 
  1680. * It is a better option to use that class and add any RTL styles to the main stylesheet. 
  1681. * 
  1682. * @since 3.0.0 
  1683. * 
  1684. * @global array $editor_styles 
  1685. * 
  1686. * @param array|string $stylesheet Optional. Stylesheet name or array thereof, relative to theme root. 
  1687. * Defaults to 'editor-style.css' 
  1688. */ 
  1689. function add_editor_style( $stylesheet = 'editor-style.css' ) { 
  1690. add_theme_support( 'editor-style' ); 
  1691.  
  1692. if ( ! is_admin() ) 
  1693. return; 
  1694.  
  1695. global $editor_styles; 
  1696. $editor_styles = (array) $editor_styles; 
  1697. $stylesheet = (array) $stylesheet; 
  1698. if ( is_rtl() ) { 
  1699. $rtl_stylesheet = str_replace('.css', '-rtl.css', $stylesheet[0]); 
  1700. $stylesheet[] = $rtl_stylesheet; 
  1701.  
  1702. $editor_styles = array_merge( $editor_styles, $stylesheet ); 
  1703.  
  1704. /** 
  1705. * Removes all visual editor stylesheets. 
  1706. * 
  1707. * @since 3.1.0 
  1708. * 
  1709. * @global array $editor_styles 
  1710. * 
  1711. * @return bool True on success, false if there were no stylesheets to remove. 
  1712. */ 
  1713. function remove_editor_styles() { 
  1714. if ( ! current_theme_supports( 'editor-style' ) ) 
  1715. return false; 
  1716. _remove_theme_support( 'editor-style' ); 
  1717. if ( is_admin() ) 
  1718. $GLOBALS['editor_styles'] = array(); 
  1719. return true; 
  1720.  
  1721. /** 
  1722. * Retrieve any registered editor stylesheets 
  1723. * 
  1724. * @since 4.0.0 
  1725. * 
  1726. * @global array $editor_styles Registered editor stylesheets 
  1727. * 
  1728. * @return array If registered, a list of editor stylesheet URLs. 
  1729. */ 
  1730. function get_editor_stylesheets() { 
  1731. $stylesheets = array(); 
  1732. // load editor_style.css if the current theme supports it 
  1733. if ( ! empty( $GLOBALS['editor_styles'] ) && is_array( $GLOBALS['editor_styles'] ) ) { 
  1734. $editor_styles = $GLOBALS['editor_styles']; 
  1735.  
  1736. $editor_styles = array_unique( array_filter( $editor_styles ) ); 
  1737. $style_uri = get_stylesheet_directory_uri(); 
  1738. $style_dir = get_stylesheet_directory(); 
  1739.  
  1740. // Support externally referenced styles (like, say, fonts). 
  1741. foreach ( $editor_styles as $key => $file ) { 
  1742. if ( preg_match( '~^(https?:)?//~', $file ) ) { 
  1743. $stylesheets[] = esc_url_raw( $file ); 
  1744. unset( $editor_styles[ $key ] ); 
  1745.  
  1746. // Look in a parent theme first, that way child theme CSS overrides. 
  1747. if ( is_child_theme() ) { 
  1748. $template_uri = get_template_directory_uri(); 
  1749. $template_dir = get_template_directory(); 
  1750.  
  1751. foreach ( $editor_styles as $key => $file ) { 
  1752. if ( $file && file_exists( "$template_dir/$file" ) ) { 
  1753. $stylesheets[] = "$template_uri/$file"; 
  1754.  
  1755. foreach ( $editor_styles as $file ) { 
  1756. if ( $file && file_exists( "$style_dir/$file" ) ) { 
  1757. $stylesheets[] = "$style_uri/$file"; 
  1758.  
  1759. /** 
  1760. * Filters the array of stylesheets applied to the editor. 
  1761. * 
  1762. * @since 4.3.0 
  1763. * 
  1764. * @param array $stylesheets Array of stylesheets to be applied to the editor. 
  1765. */ 
  1766. return apply_filters( 'editor_stylesheets', $stylesheets ); 
  1767.  
  1768. /** 
  1769. * Expand a theme's starter content configuration using core-provided data. 
  1770. * 
  1771. * @since 4.7.0 
  1772. * 
  1773. * @return array Array of starter content. 
  1774. */ 
  1775. function get_theme_starter_content() { 
  1776. $theme_support = get_theme_support( 'starter-content' ); 
  1777. if ( is_array( $theme_support ) && ! empty( $theme_support[0] ) && is_array( $theme_support[0] ) ) { 
  1778. $config = $theme_support[0]; 
  1779. } else { 
  1780. $config = array(); 
  1781.  
  1782. $core_content = array( 
  1783. 'widgets' => array( 
  1784. 'text_business_info' => array( 'text', array( 
  1785. 'title' => _x( 'Find Us', 'Theme starter content' ),  
  1786. 'text' => join( '', array( 
  1787. '<p><strong>' . _x( 'Address', 'Theme starter content' ) . '</strong><br />',  
  1788. _x( '123 Main Street', 'Theme starter content' ) . '<br />' . _x( 'New York, NY 10001', 'Theme starter content' ) . '</p>',  
  1789. '<p><strong>' . _x( 'Hours', 'Theme starter content' ) . '</strong><br />',  
  1790. _x( 'Monday—Friday: 9:00AM–5:00PM', 'Theme starter content' ) . '<br />' . _x( 'Saturday & Sunday: 11:00AM–3:00PM', 'Theme starter content' ) . '</p>' 
  1791. ) ),  
  1792. ) ),  
  1793. 'text_about' => array( 'text', array( 
  1794. 'title' => _x( 'About This Site', 'Theme starter content' ),  
  1795. 'text' => _x( 'This may be a good place to introduce yourself and your site or include some credits.', 'Theme starter content' ),  
  1796. ) ),  
  1797. 'archives' => array( 'archives', array( 
  1798. 'title' => _x( 'Archives', 'Theme starter content' ),  
  1799. ) ),  
  1800. 'calendar' => array( 'calendar', array( 
  1801. 'title' => _x( 'Calendar', 'Theme starter content' ),  
  1802. ) ),  
  1803. 'categories' => array( 'categories', array( 
  1804. 'title' => _x( 'Categories', 'Theme starter content' ),  
  1805. ) ),  
  1806. 'meta' => array( 'meta', array( 
  1807. 'title' => _x( 'Meta', 'Theme starter content' ),  
  1808. ) ),  
  1809. 'recent-comments' => array( 'recent-comments', array( 
  1810. 'title' => _x( 'Recent Comments', 'Theme starter content' ),  
  1811. ) ),  
  1812. 'recent-posts' => array( 'recent-posts', array( 
  1813. 'title' => _x( 'Recent Posts', 'Theme starter content' ),  
  1814. ) ),  
  1815. 'search' => array( 'search', array( 
  1816. 'title' => _x( 'Search', 'Theme starter content' ),  
  1817. ) ),  
  1818. ),  
  1819. 'nav_menus' => array( 
  1820. 'link_home' => array( 
  1821. 'type' => 'custom',  
  1822. 'title' => _x( 'Home', 'Theme starter content' ),  
  1823. 'url' => home_url( '/' ),  
  1824. ),  
  1825. 'page_home' => array( // Deprecated in favor of home_link. 
  1826. 'type' => 'post_type',  
  1827. 'object' => 'page',  
  1828. 'object_id' => '{{home}}',  
  1829. ),  
  1830. 'page_about' => array( 
  1831. 'type' => 'post_type',  
  1832. 'object' => 'page',  
  1833. 'object_id' => '{{about}}',  
  1834. ),  
  1835. 'page_blog' => array( 
  1836. 'type' => 'post_type',  
  1837. 'object' => 'page',  
  1838. 'object_id' => '{{blog}}',  
  1839. ),  
  1840. 'page_news' => array( 
  1841. 'type' => 'post_type',  
  1842. 'object' => 'page',  
  1843. 'object_id' => '{{news}}',  
  1844. ),  
  1845. 'page_contact' => array( 
  1846. 'type' => 'post_type',  
  1847. 'object' => 'page',  
  1848. 'object_id' => '{{contact}}',  
  1849. ),  
  1850.  
  1851. 'link_email' => array( 
  1852. 'title' => _x( 'Email', 'Theme starter content' ),  
  1853. 'url' => 'mailto:wordpress@example.com',  
  1854. ),  
  1855. 'link_facebook' => array( 
  1856. 'title' => _x( 'Facebook', 'Theme starter content' ),  
  1857. 'url' => 'https://www.facebook.com/wordpress',  
  1858. ),  
  1859. 'link_foursquare' => array( 
  1860. 'title' => _x( 'Foursquare', 'Theme starter content' ),  
  1861. 'url' => 'https://foursquare.com/',  
  1862. ),  
  1863. 'link_github' => array( 
  1864. 'title' => _x( 'GitHub', 'Theme starter content' ),  
  1865. 'url' => 'https://github.com/wordpress/',  
  1866. ),  
  1867. 'link_instagram' => array( 
  1868. 'title' => _x( 'Instagram', 'Theme starter content' ),  
  1869. 'url' => 'https://www.instagram.com/explore/tags/wordcamp/',  
  1870. ),  
  1871. 'link_linkedin' => array( 
  1872. 'title' => _x( 'LinkedIn', 'Theme starter content' ),  
  1873. 'url' => 'https://www.linkedin.com/company/1089783',  
  1874. ),  
  1875. 'link_pinterest' => array( 
  1876. 'title' => _x( 'Pinterest', 'Theme starter content' ),  
  1877. 'url' => 'https://www.pinterest.com/',  
  1878. ),  
  1879. 'link_twitter' => array( 
  1880. 'title' => _x( 'Twitter', 'Theme starter content' ),  
  1881. 'url' => 'https://twitter.com/wordpress',  
  1882. ),  
  1883. 'link_yelp' => array( 
  1884. 'title' => _x( 'Yelp', 'Theme starter content' ),  
  1885. 'url' => 'https://www.yelp.com',  
  1886. ),  
  1887. 'link_youtube' => array( 
  1888. 'title' => _x( 'YouTube', 'Theme starter content' ),  
  1889. 'url' => 'https://www.youtube.com/channel/UCdof4Ju7amm1chz1gi1T2ZA',  
  1890. ),  
  1891. ),  
  1892. 'posts' => array( 
  1893. 'home' => array( 
  1894. 'post_type' => 'page',  
  1895. 'post_title' => _x( 'Home', 'Theme starter content' ),  
  1896. 'post_content' => _x( 'Welcome to your site! This is your homepage, which is what most visitors will see when they come to your site for the first time.', 'Theme starter content' ),  
  1897. ),  
  1898. 'about' => array( 
  1899. 'post_type' => 'page',  
  1900. 'post_title' => _x( 'About', 'Theme starter content' ),  
  1901. 'post_content' => _x( 'You might be an artist who would like to introduce yourself and your work here or maybe you’re a business with a mission to describe.', 'Theme starter content' ),  
  1902. ),  
  1903. 'contact' => array( 
  1904. 'post_type' => 'page',  
  1905. 'post_title' => _x( 'Contact', 'Theme starter content' ),  
  1906. 'post_content' => _x( 'This is a page with some basic contact information, such as an address and phone number. You might also try a plugin to add a contact form.', 'Theme starter content' ),  
  1907. ),  
  1908. 'blog' => array( 
  1909. 'post_type' => 'page',  
  1910. 'post_title' => _x( 'Blog', 'Theme starter content' ),  
  1911. ),  
  1912. 'news' => array( 
  1913. 'post_type' => 'page',  
  1914. 'post_title' => _x( 'News', 'Theme starter content' ),  
  1915. ),  
  1916.  
  1917. 'homepage-section' => array( 
  1918. 'post_type' => 'page',  
  1919. 'post_title' => _x( 'A homepage section', 'Theme starter content' ),  
  1920. 'post_content' => _x( 'This is an example of a homepage section. Homepage sections can be any page other than the homepage itself, including the page that shows your latest blog posts.', 'Theme starter content' ),  
  1921. ),  
  1922. ),  
  1923. ); 
  1924.  
  1925. $content = array(); 
  1926.  
  1927. foreach ( $config as $type => $args ) { 
  1928. switch( $type ) { 
  1929. // Use options and theme_mods as-is. 
  1930. case 'options' : 
  1931. case 'theme_mods' : 
  1932. $content[ $type ] = $config[ $type ]; 
  1933. break; 
  1934.  
  1935. // Widgets are grouped into sidebars. 
  1936. case 'widgets' : 
  1937. foreach ( $config[ $type ] as $sidebar_id => $widgets ) { 
  1938. foreach ( $widgets as $id => $widget ) { 
  1939. if ( is_array( $widget ) ) { 
  1940.  
  1941. // Item extends core content. 
  1942. if ( ! empty( $core_content[ $type ][ $id ] ) ) { 
  1943. $widget = array( 
  1944. $core_content[ $type ][ $id ][0],  
  1945. array_merge( $core_content[ $type ][ $id ][1], $widget ),  
  1946. ); 
  1947.  
  1948. $content[ $type ][ $sidebar_id ][] = $widget; 
  1949. } elseif ( is_string( $widget ) && ! empty( $core_content[ $type ] ) && ! empty( $core_content[ $type ][ $widget ] ) ) { 
  1950. $content[ $type ][ $sidebar_id ][] = $core_content[ $type ][ $widget ]; 
  1951. break; 
  1952.  
  1953. // And nav menu items are grouped into nav menus. 
  1954. case 'nav_menus' : 
  1955. foreach ( $config[ $type ] as $nav_menu_location => $nav_menu ) { 
  1956.  
  1957. // Ensure nav menus get a name. 
  1958. if ( empty( $nav_menu['name'] ) ) { 
  1959. $nav_menu['name'] = $nav_menu_location; 
  1960.  
  1961. $content[ $type ][ $nav_menu_location ]['name'] = $nav_menu['name']; 
  1962.  
  1963. foreach ( $nav_menu['items'] as $id => $nav_menu_item ) { 
  1964. if ( is_array( $nav_menu_item ) ) { 
  1965.  
  1966. // Item extends core content. 
  1967. if ( ! empty( $core_content[ $type ][ $id ] ) ) { 
  1968. $nav_menu_item = array_merge( $core_content[ $type ][ $id ], $nav_menu_item ); 
  1969.  
  1970. $content[ $type ][ $nav_menu_location ]['items'][] = $nav_menu_item; 
  1971. } elseif ( is_string( $nav_menu_item ) && ! empty( $core_content[ $type ] ) && ! empty( $core_content[ $type ][ $nav_menu_item ] ) ) { 
  1972. $content[ $type ][ $nav_menu_location ]['items'][] = $core_content[ $type ][ $nav_menu_item ]; 
  1973. break; 
  1974.  
  1975. // Attachments are posts but have special treatment. 
  1976. case 'attachments' : 
  1977. foreach ( $config[ $type ] as $id => $item ) { 
  1978. if ( ! empty( $item['file'] ) ) { 
  1979. $content[ $type ][ $id ] = $item; 
  1980. break; 
  1981.  
  1982. // All that's left now are posts (besides attachments). Not a default case for the sake of clarity and future work. 
  1983. case 'posts' : 
  1984. foreach ( $config[ $type ] as $id => $item ) { 
  1985. if ( is_array( $item ) ) { 
  1986.  
  1987. // Item extends core content. 
  1988. if ( ! empty( $core_content[ $type ][ $id ] ) ) { 
  1989. $item = array_merge( $core_content[ $type ][ $id ], $item ); 
  1990.  
  1991. // Enforce a subset of fields. 
  1992. $content[ $type ][ $id ] = wp_array_slice_assoc( 
  1993. $item,  
  1994. array( 
  1995. 'post_type',  
  1996. 'post_title',  
  1997. 'post_excerpt',  
  1998. 'post_name',  
  1999. 'post_content',  
  2000. 'menu_order',  
  2001. 'comment_status',  
  2002. 'thumbnail',  
  2003. 'template',  
  2004. ); 
  2005. } elseif ( is_string( $item ) && ! empty( $core_content[ $type ][ $item ] ) ) { 
  2006. $content[ $type ][ $item ] = $core_content[ $type ][ $item ]; 
  2007. break; 
  2008.  
  2009. /** 
  2010. * Filters the expanded array of starter content. 
  2011. * 
  2012. * @since 4.7.0 
  2013. * 
  2014. * @param array $content Array of starter content. 
  2015. * @param array $config Array of theme-specific starter content configuration. 
  2016. */ 
  2017. return apply_filters( 'get_theme_starter_content', $content, $config ); 
  2018.  
  2019. /** 
  2020. * Registers theme support for a given feature. 
  2021. * 
  2022. * Must be called in the theme's functions.php file to work. 
  2023. * If attached to a hook, it must be {@see 'after_setup_theme'}. 
  2024. * The {@see 'init'} hook may be too late for some features. 
  2025. * 
  2026. * @since 2.9.0 
  2027. * @since 3.6.0 The `html5` feature was added 
  2028. * @since 3.9.0 The `html5` feature now also accepts 'gallery' and 'caption' 
  2029. * @since 4.1.0 The `title-tag` feature was added 
  2030. * @since 4.5.0 The `customize-selective-refresh-widgets` feature was added 
  2031. * @since 4.7.0 The `starter-content` feature was added 
  2032. * 
  2033. * @global array $_wp_theme_features 
  2034. * 
  2035. * @param string $feature The feature being added. Likely core values include 'post-formats',  
  2036. * 'post-thumbnails', 'html5', 'custom-logo', 'custom-header-uploads',  
  2037. * 'custom-header', 'custom-background', 'title-tag', 'starter-content', etc. 
  2038. * @param mixed $args, ... Optional extra arguments to pass along with certain features. 
  2039. * @return void|bool False on failure, void otherwise. 
  2040. */ 
  2041. function add_theme_support( $feature ) { 
  2042. global $_wp_theme_features; 
  2043.  
  2044. if ( func_num_args() == 1 ) 
  2045. $args = true; 
  2046. else 
  2047. $args = array_slice( func_get_args(), 1 ); 
  2048.  
  2049. switch ( $feature ) { 
  2050. case 'post-thumbnails': 
  2051. // All post types are already supported. 
  2052. if ( true === get_theme_support( 'post-thumbnails' ) ) { 
  2053. return; 
  2054.  
  2055. /** 
  2056. * Merge post types with any that already declared their support 
  2057. * for post thumbnails. 
  2058. */ 
  2059. if ( is_array( $args[0] ) && isset( $_wp_theme_features['post-thumbnails'] ) ) { 
  2060. $args[0] = array_unique( array_merge( $_wp_theme_features['post-thumbnails'][0], $args[0] ) ); 
  2061.  
  2062. break; 
  2063.  
  2064. case 'post-formats' : 
  2065. if ( is_array( $args[0] ) ) { 
  2066. $post_formats = get_post_format_slugs(); 
  2067. unset( $post_formats['standard'] ); 
  2068.  
  2069. $args[0] = array_intersect( $args[0], array_keys( $post_formats ) ); 
  2070. break; 
  2071.  
  2072. case 'html5' : 
  2073. // You can't just pass 'html5', you need to pass an array of types. 
  2074. if ( empty( $args[0] ) ) { 
  2075. // Build an array of types for back-compat. 
  2076. $args = array( 0 => array( 'comment-list', 'comment-form', 'search-form' ) ); 
  2077. } elseif ( ! is_array( $args[0] ) ) { 
  2078. _doing_it_wrong( "add_theme_support( 'html5' )", __( 'You need to pass an array of types.' ), '3.6.1' ); 
  2079. return false; 
  2080.  
  2081. // Calling 'html5' again merges, rather than overwrites. 
  2082. if ( isset( $_wp_theme_features['html5'] ) ) 
  2083. $args[0] = array_merge( $_wp_theme_features['html5'][0], $args[0] ); 
  2084. break; 
  2085.  
  2086. case 'custom-logo': 
  2087. if ( ! is_array( $args ) ) { 
  2088. $args = array( 0 => array() ); 
  2089. $defaults = array( 
  2090. 'width' => null,  
  2091. 'height' => null,  
  2092. 'flex-width' => false,  
  2093. 'flex-height' => false,  
  2094. 'header-text' => '',  
  2095. ); 
  2096. $args[0] = wp_parse_args( array_intersect_key( $args[0], $defaults ), $defaults ); 
  2097.  
  2098. // Allow full flexibility if no size is specified. 
  2099. if ( is_null( $args[0]['width'] ) && is_null( $args[0]['height'] ) ) { 
  2100. $args[0]['flex-width'] = true; 
  2101. $args[0]['flex-height'] = true; 
  2102. break; 
  2103.  
  2104. case 'custom-header-uploads' : 
  2105. return add_theme_support( 'custom-header', array( 'uploads' => true ) ); 
  2106.  
  2107. case 'custom-header' : 
  2108. if ( ! is_array( $args ) ) 
  2109. $args = array( 0 => array() ); 
  2110.  
  2111. $defaults = array( 
  2112. 'default-image' => '',  
  2113. 'random-default' => false,  
  2114. 'width' => 0,  
  2115. 'height' => 0,  
  2116. 'flex-height' => false,  
  2117. 'flex-width' => false,  
  2118. 'default-text-color' => '',  
  2119. 'header-text' => true,  
  2120. 'uploads' => true,  
  2121. 'wp-head-callback' => '',  
  2122. 'admin-head-callback' => '',  
  2123. 'admin-preview-callback' => '',  
  2124. 'video' => false,  
  2125. 'video-active-callback' => 'is_front_page',  
  2126. ); 
  2127.  
  2128. $jit = isset( $args[0]['__jit'] ); 
  2129. unset( $args[0]['__jit'] ); 
  2130.  
  2131. // Merge in data from previous add_theme_support() calls. 
  2132. // The first value registered wins. (A child theme is set up first.) 
  2133. if ( isset( $_wp_theme_features['custom-header'] ) ) 
  2134. $args[0] = wp_parse_args( $_wp_theme_features['custom-header'][0], $args[0] ); 
  2135.  
  2136. // Load in the defaults at the end, as we need to insure first one wins. 
  2137. // This will cause all constants to be defined, as each arg will then be set to the default. 
  2138. if ( $jit ) 
  2139. $args[0] = wp_parse_args( $args[0], $defaults ); 
  2140.  
  2141. // If a constant was defined, use that value. Otherwise, define the constant to ensure 
  2142. // the constant is always accurate (and is not defined later, overriding our value). 
  2143. // As stated above, the first value wins. 
  2144. // Once we get to wp_loaded (just-in-time), define any constants we haven't already. 
  2145. // Constants are lame. Don't reference them. This is just for backward compatibility. 
  2146.  
  2147. if ( defined( 'NO_HEADER_TEXT' ) ) 
  2148. $args[0]['header-text'] = ! NO_HEADER_TEXT; 
  2149. elseif ( isset( $args[0]['header-text'] ) ) 
  2150. define( 'NO_HEADER_TEXT', empty( $args[0]['header-text'] ) ); 
  2151.  
  2152. if ( defined( 'HEADER_IMAGE_WIDTH' ) ) 
  2153. $args[0]['width'] = (int) HEADER_IMAGE_WIDTH; 
  2154. elseif ( isset( $args[0]['width'] ) ) 
  2155. define( 'HEADER_IMAGE_WIDTH', (int) $args[0]['width'] ); 
  2156.  
  2157. if ( defined( 'HEADER_IMAGE_HEIGHT' ) ) 
  2158. $args[0]['height'] = (int) HEADER_IMAGE_HEIGHT; 
  2159. elseif ( isset( $args[0]['height'] ) ) 
  2160. define( 'HEADER_IMAGE_HEIGHT', (int) $args[0]['height'] ); 
  2161.  
  2162. if ( defined( 'HEADER_TEXTCOLOR' ) ) 
  2163. $args[0]['default-text-color'] = HEADER_TEXTCOLOR; 
  2164. elseif ( isset( $args[0]['default-text-color'] ) ) 
  2165. define( 'HEADER_TEXTCOLOR', $args[0]['default-text-color'] ); 
  2166.  
  2167. if ( defined( 'HEADER_IMAGE' ) ) 
  2168. $args[0]['default-image'] = HEADER_IMAGE; 
  2169. elseif ( isset( $args[0]['default-image'] ) ) 
  2170. define( 'HEADER_IMAGE', $args[0]['default-image'] ); 
  2171.  
  2172. if ( $jit && ! empty( $args[0]['default-image'] ) ) 
  2173. $args[0]['random-default'] = false; 
  2174.  
  2175. // If headers are supported, and we still don't have a defined width or height,  
  2176. // we have implicit flex sizes. 
  2177. if ( $jit ) { 
  2178. if ( empty( $args[0]['width'] ) && empty( $args[0]['flex-width'] ) ) 
  2179. $args[0]['flex-width'] = true; 
  2180. if ( empty( $args[0]['height'] ) && empty( $args[0]['flex-height'] ) ) 
  2181. $args[0]['flex-height'] = true; 
  2182.  
  2183. break; 
  2184.  
  2185. case 'custom-background' : 
  2186. if ( ! is_array( $args ) ) 
  2187. $args = array( 0 => array() ); 
  2188.  
  2189. $defaults = array( 
  2190. 'default-image' => '',  
  2191. 'default-preset' => 'default',  
  2192. 'default-position-x' => 'left',  
  2193. 'default-position-y' => 'top',  
  2194. 'default-size' => 'auto',  
  2195. 'default-repeat' => 'repeat',  
  2196. 'default-attachment' => 'scroll',  
  2197. 'default-color' => '',  
  2198. 'wp-head-callback' => '_custom_background_cb',  
  2199. 'admin-head-callback' => '',  
  2200. 'admin-preview-callback' => '',  
  2201. ); 
  2202.  
  2203. $jit = isset( $args[0]['__jit'] ); 
  2204. unset( $args[0]['__jit'] ); 
  2205.  
  2206. // Merge in data from previous add_theme_support() calls. The first value registered wins. 
  2207. if ( isset( $_wp_theme_features['custom-background'] ) ) 
  2208. $args[0] = wp_parse_args( $_wp_theme_features['custom-background'][0], $args[0] ); 
  2209.  
  2210. if ( $jit ) 
  2211. $args[0] = wp_parse_args( $args[0], $defaults ); 
  2212.  
  2213. if ( defined( 'BACKGROUND_COLOR' ) ) 
  2214. $args[0]['default-color'] = BACKGROUND_COLOR; 
  2215. elseif ( isset( $args[0]['default-color'] ) || $jit ) 
  2216. define( 'BACKGROUND_COLOR', $args[0]['default-color'] ); 
  2217.  
  2218. if ( defined( 'BACKGROUND_IMAGE' ) ) 
  2219. $args[0]['default-image'] = BACKGROUND_IMAGE; 
  2220. elseif ( isset( $args[0]['default-image'] ) || $jit ) 
  2221. define( 'BACKGROUND_IMAGE', $args[0]['default-image'] ); 
  2222.  
  2223. break; 
  2224.  
  2225. // Ensure that 'title-tag' is accessible in the admin. 
  2226. case 'title-tag' : 
  2227. // Can be called in functions.php but must happen before wp_loaded, i.e. not in header.php. 
  2228. if ( did_action( 'wp_loaded' ) ) { 
  2229. /** translators: 1: Theme support 2: hook name */ 
  2230. _doing_it_wrong( "add_theme_support( 'title-tag' )", sprintf( __( 'Theme support for %1$s should be registered before the %2$s hook.' ),  
  2231. '<code>title-tag</code>', '<code>wp_loaded</code>' ), '4.1.0' ); 
  2232.  
  2233. return false; 
  2234.  
  2235. $_wp_theme_features[ $feature ] = $args; 
  2236.  
  2237. /** 
  2238. * Registers the internal custom header and background routines. 
  2239. * 
  2240. * @since 3.4.0 
  2241. * @access private 
  2242. * 
  2243. * @global Custom_Image_Header $custom_image_header 
  2244. * @global Custom_Background $custom_background 
  2245. */ 
  2246. function _custom_header_background_just_in_time() { 
  2247. global $custom_image_header, $custom_background; 
  2248.  
  2249. if ( current_theme_supports( 'custom-header' ) ) { 
  2250. // In case any constants were defined after an add_custom_image_header() call, re-run. 
  2251. add_theme_support( 'custom-header', array( '__jit' => true ) ); 
  2252.  
  2253. $args = get_theme_support( 'custom-header' ); 
  2254. if ( $args[0]['wp-head-callback'] ) 
  2255. add_action( 'wp_head', $args[0]['wp-head-callback'] ); 
  2256.  
  2257. if ( is_admin() ) { 
  2258. require_once( ABSPATH . 'wp-admin/custom-header.php' ); 
  2259. $custom_image_header = new Custom_Image_Header( $args[0]['admin-head-callback'], $args[0]['admin-preview-callback'] ); 
  2260.  
  2261. if ( current_theme_supports( 'custom-background' ) ) { 
  2262. // In case any constants were defined after an add_custom_background() call, re-run. 
  2263. add_theme_support( 'custom-background', array( '__jit' => true ) ); 
  2264.  
  2265. $args = get_theme_support( 'custom-background' ); 
  2266. add_action( 'wp_head', $args[0]['wp-head-callback'] ); 
  2267.  
  2268. if ( is_admin() ) { 
  2269. require_once( ABSPATH . 'wp-admin/custom-background.php' ); 
  2270. $custom_background = new Custom_Background( $args[0]['admin-head-callback'], $args[0]['admin-preview-callback'] ); 
  2271.  
  2272. /** 
  2273. * Adds CSS to hide header text for custom logo, based on Customizer setting. 
  2274. * 
  2275. * @since 4.5.0 
  2276. * @access private 
  2277. */ 
  2278. function _custom_logo_header_styles() { 
  2279. if ( ! current_theme_supports( 'custom-header', 'header-text' ) && get_theme_support( 'custom-logo', 'header-text' ) && ! get_theme_mod( 'header_text', true ) ) { 
  2280. $classes = (array) get_theme_support( 'custom-logo', 'header-text' ); 
  2281. $classes = array_map( 'sanitize_html_class', $classes ); 
  2282. $classes = '.' . implode( ', .', $classes ); 
  2283.  
  2284. ?> 
  2285. <!-- Custom Logo: hide header text --> 
  2286. <style id="custom-logo-css" type="text/css"> 
  2287. <?php echo $classes; ?> { 
  2288. position: absolute; 
  2289. clip: rect(1px, 1px, 1px, 1px); 
  2290. </style> 
  2291. <?php 
  2292.  
  2293. /** 
  2294. * Gets the theme support arguments passed when registering that support 
  2295. * 
  2296. * @since 3.1.0 
  2297. * 
  2298. * @global array $_wp_theme_features 
  2299. * 
  2300. * @param string $feature the feature to check 
  2301. * @return mixed The array of extra arguments or the value for the registered feature. 
  2302. */ 
  2303. function get_theme_support( $feature ) { 
  2304. global $_wp_theme_features; 
  2305. if ( ! isset( $_wp_theme_features[ $feature ] ) ) 
  2306. return false; 
  2307.  
  2308. if ( func_num_args() <= 1 ) 
  2309. return $_wp_theme_features[ $feature ]; 
  2310.  
  2311. $args = array_slice( func_get_args(), 1 ); 
  2312. switch ( $feature ) { 
  2313. case 'custom-logo' : 
  2314. case 'custom-header' : 
  2315. case 'custom-background' : 
  2316. if ( isset( $_wp_theme_features[ $feature ][0][ $args[0] ] ) ) 
  2317. return $_wp_theme_features[ $feature ][0][ $args[0] ]; 
  2318. return false; 
  2319.  
  2320. default : 
  2321. return $_wp_theme_features[ $feature ]; 
  2322.  
  2323. /** 
  2324. * Allows a theme to de-register its support of a certain feature 
  2325. * 
  2326. * Should be called in the theme's functions.php file. Generally would 
  2327. * be used for child themes to override support from the parent theme. 
  2328. * 
  2329. * @since 3.0.0 
  2330. * @see add_theme_support() 
  2331. * @param string $feature the feature being added 
  2332. * @return bool|void Whether feature was removed. 
  2333. */ 
  2334. function remove_theme_support( $feature ) { 
  2335. // Blacklist: for internal registrations not used directly by themes. 
  2336. if ( in_array( $feature, array( 'editor-style', 'widgets', 'menus' ) ) ) 
  2337. return false; 
  2338.  
  2339. return _remove_theme_support( $feature ); 
  2340.  
  2341. /** 
  2342. * Do not use. Removes theme support internally, ignorant of the blacklist. 
  2343. * 
  2344. * @access private 
  2345. * @since 3.1.0 
  2346. * 
  2347. * @global array $_wp_theme_features 
  2348. * @global Custom_Image_Header $custom_image_header 
  2349. * @global Custom_Background $custom_background 
  2350. * 
  2351. * @param string $feature 
  2352. */ 
  2353. function _remove_theme_support( $feature ) { 
  2354. global $_wp_theme_features; 
  2355.  
  2356. switch ( $feature ) { 
  2357. case 'custom-header-uploads' : 
  2358. if ( ! isset( $_wp_theme_features['custom-header'] ) ) 
  2359. return false; 
  2360. add_theme_support( 'custom-header', array( 'uploads' => false ) ); 
  2361. return; // Do not continue - custom-header-uploads no longer exists. 
  2362.  
  2363. if ( ! isset( $_wp_theme_features[ $feature ] ) ) 
  2364. return false; 
  2365.  
  2366. switch ( $feature ) { 
  2367. case 'custom-header' : 
  2368. if ( ! did_action( 'wp_loaded' ) ) 
  2369. break; 
  2370. $support = get_theme_support( 'custom-header' ); 
  2371. if ( isset( $support[0]['wp-head-callback'] ) ) { 
  2372. remove_action( 'wp_head', $support[0]['wp-head-callback'] ); 
  2373. if ( isset( $GLOBALS['custom_image_header'] ) ) { 
  2374. remove_action( 'admin_menu', array( $GLOBALS['custom_image_header'], 'init' ) ); 
  2375. unset( $GLOBALS['custom_image_header'] ); 
  2376. break; 
  2377.  
  2378. case 'custom-background' : 
  2379. if ( ! did_action( 'wp_loaded' ) ) 
  2380. break; 
  2381. $support = get_theme_support( 'custom-background' ); 
  2382. remove_action( 'wp_head', $support[0]['wp-head-callback'] ); 
  2383. remove_action( 'admin_menu', array( $GLOBALS['custom_background'], 'init' ) ); 
  2384. unset( $GLOBALS['custom_background'] ); 
  2385. break; 
  2386.  
  2387. unset( $_wp_theme_features[ $feature ] ); 
  2388. return true; 
  2389.  
  2390. /** 
  2391. * Checks a theme's support for a given feature 
  2392. * 
  2393. * @since 2.9.0 
  2394. * 
  2395. * @global array $_wp_theme_features 
  2396. * 
  2397. * @param string $feature the feature being checked 
  2398. * @return bool 
  2399. */ 
  2400. function current_theme_supports( $feature ) { 
  2401. global $_wp_theme_features; 
  2402.  
  2403. if ( 'custom-header-uploads' == $feature ) 
  2404. return current_theme_supports( 'custom-header', 'uploads' ); 
  2405.  
  2406. if ( !isset( $_wp_theme_features[$feature] ) ) 
  2407. return false; 
  2408.  
  2409. // If no args passed then no extra checks need be performed 
  2410. if ( func_num_args() <= 1 ) 
  2411. return true; 
  2412.  
  2413. $args = array_slice( func_get_args(), 1 ); 
  2414.  
  2415. switch ( $feature ) { 
  2416. case 'post-thumbnails': 
  2417. // post-thumbnails can be registered for only certain content/post types by passing 
  2418. // an array of types to add_theme_support(). If no array was passed, then 
  2419. // any type is accepted 
  2420. if ( true === $_wp_theme_features[$feature] ) // Registered for all types 
  2421. return true; 
  2422. $content_type = $args[0]; 
  2423. return in_array( $content_type, $_wp_theme_features[$feature][0] ); 
  2424.  
  2425. case 'html5': 
  2426. case 'post-formats': 
  2427. // specific post formats can be registered by passing an array of types to 
  2428. // add_theme_support() 
  2429.  
  2430. // Specific areas of HTML5 support *must* be passed via an array to add_theme_support() 
  2431.  
  2432. $type = $args[0]; 
  2433. return in_array( $type, $_wp_theme_features[$feature][0] ); 
  2434.  
  2435. case 'custom-logo': 
  2436. case 'custom-header': 
  2437. case 'custom-background': 
  2438. // Specific capabilities can be registered by passing an array to add_theme_support(). 
  2439. return ( isset( $_wp_theme_features[ $feature ][0][ $args[0] ] ) && $_wp_theme_features[ $feature ][0][ $args[0] ] ); 
  2440.  
  2441. /** 
  2442. * Filters whether the current theme supports a specific feature. 
  2443. * 
  2444. * The dynamic portion of the hook name, `$feature`, refers to the specific theme 
  2445. * feature. Possible values include 'post-formats', 'post-thumbnails', 'custom-background',  
  2446. * 'custom-header', 'menus', 'automatic-feed-links', 'html5',  
  2447. * 'starter-content', and 'customize-selective-refresh-widgets'. 
  2448. * 
  2449. * @since 3.4.0 
  2450. * 
  2451. * @param bool true Whether the current theme supports the given feature. Default true. 
  2452. * @param array $args Array of arguments for the feature. 
  2453. * @param string $feature The theme feature. 
  2454. */ 
  2455. return apply_filters( "current_theme_supports-{$feature}", true, $args, $_wp_theme_features[$feature] ); 
  2456.  
  2457. /** 
  2458. * Checks a theme's support for a given feature before loading the functions which implement it. 
  2459. * 
  2460. * @since 2.9.0 
  2461. * 
  2462. * @param string $feature The feature being checked. 
  2463. * @param string $include Path to the file. 
  2464. * @return bool True if the current theme supports the supplied feature, false otherwise. 
  2465. */ 
  2466. function require_if_theme_supports( $feature, $include ) { 
  2467. if ( current_theme_supports( $feature ) ) { 
  2468. require ( $include ); 
  2469. return true; 
  2470. return false; 
  2471.  
  2472. /** 
  2473. * Checks an attachment being deleted to see if it's a header or background image. 
  2474. * 
  2475. * If true it removes the theme modification which would be pointing at the deleted 
  2476. * attachment. 
  2477. * 
  2478. * @access private 
  2479. * @since 3.0.0 
  2480. * @since 4.3.0 Also removes `header_image_data`. 
  2481. * @since 4.5.0 Also removes custom logo theme mods. 
  2482. * 
  2483. * @param int $id The attachment id. 
  2484. */ 
  2485. function _delete_attachment_theme_mod( $id ) { 
  2486. $attachment_image = wp_get_attachment_url( $id ); 
  2487. $header_image = get_header_image(); 
  2488. $background_image = get_background_image(); 
  2489. $custom_logo_id = get_theme_mod( 'custom_logo' ); 
  2490.  
  2491. if ( $custom_logo_id && $custom_logo_id == $id ) { 
  2492. remove_theme_mod( 'custom_logo' ); 
  2493. remove_theme_mod( 'header_text' ); 
  2494.  
  2495. if ( $header_image && $header_image == $attachment_image ) { 
  2496. remove_theme_mod( 'header_image' ); 
  2497. remove_theme_mod( 'header_image_data' ); 
  2498.  
  2499. if ( $background_image && $background_image == $attachment_image ) { 
  2500. remove_theme_mod( 'background_image' ); 
  2501.  
  2502. /** 
  2503. * Checks if a theme has been changed and runs 'after_switch_theme' hook on the next WP load. 
  2504. * 
  2505. * See {@see 'after_switch_theme'}. 
  2506. * 
  2507. * @since 3.3.0 
  2508. */ 
  2509. function check_theme_switched() { 
  2510. if ( $stylesheet = get_option( 'theme_switched' ) ) { 
  2511. $old_theme = wp_get_theme( $stylesheet ); 
  2512.  
  2513. // Prevent retrieve_widgets() from running since Customizer already called it up front 
  2514. if ( get_option( 'theme_switched_via_customizer' ) ) { 
  2515. remove_action( 'after_switch_theme', '_wp_sidebars_changed' ); 
  2516. update_option( 'theme_switched_via_customizer', false ); 
  2517.  
  2518. if ( $old_theme->exists() ) { 
  2519. /** 
  2520. * Fires on the first WP load after a theme switch if the old theme still exists. 
  2521. * 
  2522. * This action fires multiple times and the parameters differs 
  2523. * according to the context, if the old theme exists or not. 
  2524. * If the old theme is missing, the parameter will be the slug 
  2525. * of the old theme. 
  2526. * 
  2527. * @since 3.3.0 
  2528. * 
  2529. * @param string $old_name Old theme name. 
  2530. * @param WP_Theme $old_theme WP_Theme instance of the old theme. 
  2531. */ 
  2532. do_action( 'after_switch_theme', $old_theme->get( 'Name' ), $old_theme ); 
  2533. } else { 
  2534. /** This action is documented in wp-includes/theme.php */ 
  2535. do_action( 'after_switch_theme', $stylesheet ); 
  2536. flush_rewrite_rules(); 
  2537.  
  2538. update_option( 'theme_switched', false ); 
  2539.  
  2540. /** 
  2541. * Includes and instantiates the WP_Customize_Manager class. 
  2542. * 
  2543. * Loads the Customizer at plugins_loaded when accessing the customize.php admin 
  2544. * page or when any request includes a wp_customize=on param or a customize_changeset 
  2545. * param (a UUID). This param is a signal for whether to bootstrap the Customizer when 
  2546. * WordPress is loading, especially in the Customizer preview 
  2547. * or when making Customizer Ajax requests for widgets or menus. 
  2548. * 
  2549. * @since 3.4.0 
  2550. * 
  2551. * @global WP_Customize_Manager $wp_customize 
  2552. */ 
  2553. function _wp_customize_include() { 
  2554.  
  2555. $is_customize_admin_page = ( is_admin() && 'customize.php' == basename( $_SERVER['PHP_SELF'] ) ); 
  2556. $should_include = ( 
  2557. $is_customize_admin_page 
  2558. || 
  2559. ( isset( $_REQUEST['wp_customize'] ) && 'on' == $_REQUEST['wp_customize'] ) 
  2560. || 
  2561. ( ! empty( $_GET['customize_changeset_uuid'] ) || ! empty( $_POST['customize_changeset_uuid'] ) ) 
  2562. ); 
  2563.  
  2564. if ( ! $should_include ) { 
  2565. return; 
  2566.  
  2567. /** 
  2568. * Note that wp_unslash() is not being used on the input vars because it is 
  2569. * called before wp_magic_quotes() gets called. Besides this fact, none of 
  2570. * the values should contain any characters needing slashes anyway. 
  2571. */ 
  2572. $keys = array( 'changeset_uuid', 'customize_changeset_uuid', 'customize_theme', 'theme', 'customize_messenger_channel' ); 
  2573. $input_vars = array_merge( 
  2574. wp_array_slice_assoc( $_GET, $keys ),  
  2575. wp_array_slice_assoc( $_POST, $keys ) 
  2576. ); 
  2577.  
  2578. $theme = null; 
  2579. $changeset_uuid = null; 
  2580. $messenger_channel = null; 
  2581.  
  2582. if ( $is_customize_admin_page && isset( $input_vars['changeset_uuid'] ) ) { 
  2583. $changeset_uuid = sanitize_key( $input_vars['changeset_uuid'] ); 
  2584. } elseif ( ! empty( $input_vars['customize_changeset_uuid'] ) ) { 
  2585. $changeset_uuid = sanitize_key( $input_vars['customize_changeset_uuid'] ); 
  2586.  
  2587. // Note that theme will be sanitized via WP_Theme. 
  2588. if ( $is_customize_admin_page && isset( $input_vars['theme'] ) ) { 
  2589. $theme = $input_vars['theme']; 
  2590. } elseif ( isset( $input_vars['customize_theme'] ) ) { 
  2591. $theme = $input_vars['customize_theme']; 
  2592. if ( isset( $input_vars['customize_messenger_channel'] ) ) { 
  2593. $messenger_channel = sanitize_key( $input_vars['customize_messenger_channel'] ); 
  2594.  
  2595. require_once ABSPATH . WPINC . '/class-wp-customize-manager.php'; 
  2596. $GLOBALS['wp_customize'] = new WP_Customize_Manager( compact( 'changeset_uuid', 'theme', 'messenger_channel' ) ); 
  2597.  
  2598. /** 
  2599. * Publish a snapshot's changes. 
  2600. * 
  2601. * @param string $new_status New post status. 
  2602. * @param string $old_status Old post status. 
  2603. * @param WP_Post $changeset_post Changeset post object. 
  2604. */ 
  2605. function _wp_customize_publish_changeset( $new_status, $old_status, $changeset_post ) { 
  2606. global $wp_customize, $wpdb; 
  2607.  
  2608. $is_publishing_changeset = ( 
  2609. 'customize_changeset' === $changeset_post->post_type 
  2610. && 
  2611. 'publish' === $new_status 
  2612. && 
  2613. 'publish' !== $old_status 
  2614. ); 
  2615. if ( ! $is_publishing_changeset ) { 
  2616. return; 
  2617.  
  2618. if ( empty( $wp_customize ) ) { 
  2619. require_once ABSPATH . WPINC . '/class-wp-customize-manager.php'; 
  2620. $wp_customize = new WP_Customize_Manager( array( 'changeset_uuid' => $changeset_post->post_name ) ); 
  2621.  
  2622. if ( ! did_action( 'customize_register' ) ) { 
  2623. /** 
  2624. * When running from CLI or Cron, the customize_register action will need 
  2625. * to be triggered in order for core, themes, and plugins to register their 
  2626. * settings. Normally core will add_action( 'customize_register' ) at 
  2627. * priority 10 to register the core settings, and if any themes/plugins 
  2628. * also add_action( 'customize_register' ) at the same priority, they 
  2629. * will have a $wp_customize with those settings registered since they 
  2630. * call add_action() afterward, normally. However, when manually doing 
  2631. * the customize_register action after the setup_theme, then the order 
  2632. * will be reversed for two actions added at priority 10, resulting in 
  2633. * the core settings no longer being available as expected to themes/plugins. 
  2634. * So the following manually calls the method that registers the core 
  2635. * settings up front before doing the action. 
  2636. */ 
  2637. remove_action( 'customize_register', array( $wp_customize, 'register_controls' ) ); 
  2638. $wp_customize->register_controls(); 
  2639.  
  2640. /** This filter is documented in /wp-includes/class-wp-customize-manager.php */ 
  2641. do_action( 'customize_register', $wp_customize ); 
  2642. $wp_customize->_publish_changeset_values( $changeset_post->ID ) ; 
  2643.  
  2644. /** 
  2645. * Trash the changeset post if revisions are not enabled. Unpublished 
  2646. * changesets by default get garbage collected due to the auto-draft status. 
  2647. * When a changeset post is published, however, it would no longer get cleaned 
  2648. * out. Ths is a problem when the changeset posts are never displayed anywhere,  
  2649. * since they would just be endlessly piling up. So here we use the revisions 
  2650. * feature to indicate whether or not a published changeset should get trashed 
  2651. * and thus garbage collected. 
  2652. */ 
  2653. if ( ! wp_revisions_enabled( $changeset_post ) ) { 
  2654. $post = $changeset_post; 
  2655. $post_id = $changeset_post->ID; 
  2656.  
  2657. /** 
  2658. * The following re-formulates the logic from wp_trash_post() as done in 
  2659. * wp_publish_post(). The reason for bypassing wp_trash_post() is that it 
  2660. * will mutate the the post_content and the post_name when they should be 
  2661. * untouched. 
  2662. */ 
  2663. if ( ! EMPTY_TRASH_DAYS ) { 
  2664. wp_delete_post( $post_id, true ); 
  2665. } else { 
  2666. /** This action is documented in wp-includes/post.php */ 
  2667. do_action( 'wp_trash_post', $post_id ); 
  2668.  
  2669. add_post_meta( $post_id, '_wp_trash_meta_status', $post->post_status ); 
  2670. add_post_meta( $post_id, '_wp_trash_meta_time', time() ); 
  2671.  
  2672. $old_status = $post->post_status; 
  2673. $new_status = 'trash'; 
  2674. $wpdb->update( $wpdb->posts, array( 'post_status' => $new_status ), array( 'ID' => $post->ID ) ); 
  2675. clean_post_cache( $post->ID ); 
  2676.  
  2677. $post->post_status = $new_status; 
  2678. wp_transition_post_status( $new_status, $old_status, $post ); 
  2679.  
  2680. /** This action is documented in wp-includes/post.php */ 
  2681. do_action( 'edit_post', $post->ID, $post ); 
  2682.  
  2683. /** This action is documented in wp-includes/post.php */ 
  2684. do_action( "save_post_{$post->post_type}", $post->ID, $post, true ); 
  2685.  
  2686. /** This action is documented in wp-includes/post.php */ 
  2687. do_action( 'save_post', $post->ID, $post, true ); 
  2688.  
  2689. /** This action is documented in wp-includes/post.php */ 
  2690. do_action( 'wp_insert_post', $post->ID, $post, true ); 
  2691.  
  2692. /** This action is documented in wp-includes/post.php */ 
  2693. do_action( 'trashed_post', $post_id ); 
  2694.  
  2695. /** 
  2696. * Filters changeset post data upon insert to ensure post_name is intact. 
  2697. * 
  2698. * This is needed to prevent the post_name from being dropped when the post is 
  2699. * transitioned into pending status by a contributor. 
  2700. * 
  2701. * @since 4.7.0 
  2702. * @see wp_insert_post() 
  2703. * 
  2704. * @param array $post_data An array of slashed post data. 
  2705. * @param array $supplied_post_data An array of sanitized, but otherwise unmodified post data. 
  2706. * @returns array Filtered data. 
  2707. */ 
  2708. function _wp_customize_changeset_filter_insert_post_data( $post_data, $supplied_post_data ) { 
  2709. if ( isset( $post_data['post_type'] ) && 'customize_changeset' === $post_data['post_type'] ) { 
  2710.  
  2711. // Prevent post_name from being dropped, such as when contributor saves a changeset post as pending. 
  2712. if ( empty( $post_data['post_name'] ) && ! empty( $supplied_post_data['post_name'] ) ) { 
  2713. $post_data['post_name'] = $supplied_post_data['post_name']; 
  2714. return $post_data; 
  2715.  
  2716. /** 
  2717. * Adds settings for the customize-loader script. 
  2718. * 
  2719. * @since 3.4.0 
  2720. */ 
  2721. function _wp_customize_loader_settings() { 
  2722. $admin_origin = parse_url( admin_url() ); 
  2723. $home_origin = parse_url( home_url() ); 
  2724. $cross_domain = ( strtolower( $admin_origin[ 'host' ] ) != strtolower( $home_origin[ 'host' ] ) ); 
  2725.  
  2726. $browser = array( 
  2727. 'mobile' => wp_is_mobile(),  
  2728. 'ios' => wp_is_mobile() && preg_match( '/iPad|iPod|iPhone/', $_SERVER['HTTP_USER_AGENT'] ),  
  2729. ); 
  2730.  
  2731. $settings = array( 
  2732. 'url' => esc_url( admin_url( 'customize.php' ) ),  
  2733. 'isCrossDomain' => $cross_domain,  
  2734. 'browser' => $browser,  
  2735. 'l10n' => array( 
  2736. 'saveAlert' => __( 'The changes you made will be lost if you navigate away from this page.' ),  
  2737. 'mainIframeTitle' => __( 'Customizer' ),  
  2738. ),  
  2739. ); 
  2740.  
  2741. $script = 'var _wpCustomizeLoaderSettings = ' . wp_json_encode( $settings ) . ';'; 
  2742.  
  2743. $wp_scripts = wp_scripts(); 
  2744. $data = $wp_scripts->get_data( 'customize-loader', 'data' ); 
  2745. if ( $data ) 
  2746. $script = "$data\n$script"; 
  2747.  
  2748. $wp_scripts->add_data( 'customize-loader', 'data', $script ); 
  2749.  
  2750. /** 
  2751. * Returns a URL to load the Customizer. 
  2752. * 
  2753. * @since 3.4.0 
  2754. * 
  2755. * @param string $stylesheet Optional. Theme to customize. Defaults to current theme. 
  2756. * The theme's stylesheet will be urlencoded if necessary. 
  2757. * @return string 
  2758. */ 
  2759. function wp_customize_url( $stylesheet = null ) { 
  2760. $url = admin_url( 'customize.php' ); 
  2761. if ( $stylesheet ) 
  2762. $url .= '?theme=' . urlencode( $stylesheet ); 
  2763. return esc_url( $url ); 
  2764.  
  2765. /** 
  2766. * Prints a script to check whether or not the Customizer is supported,  
  2767. * and apply either the no-customize-support or customize-support class 
  2768. * to the body. 
  2769. * 
  2770. * This function MUST be called inside the body tag. 
  2771. * 
  2772. * Ideally, call this function immediately after the body tag is opened. 
  2773. * This prevents a flash of unstyled content. 
  2774. * 
  2775. * It is also recommended that you add the "no-customize-support" class 
  2776. * to the body tag by default. 
  2777. * 
  2778. * @since 3.4.0 
  2779. * @since 4.7.0 Support for IE8 and below is explicitly removed via conditional comments. 
  2780. */ 
  2781. function wp_customize_support_script() { 
  2782. $admin_origin = parse_url( admin_url() ); 
  2783. $home_origin = parse_url( home_url() ); 
  2784. $cross_domain = ( strtolower( $admin_origin[ 'host' ] ) != strtolower( $home_origin[ 'host' ] ) ); 
  2785.  
  2786. ?> 
  2787. <!--[if lte IE 8]> 
  2788. <script type="text/javascript"> 
  2789. document.body.className = document.body.className.replace( /(^|\s)(no-)?customize-support(?=\s|$)/, '' ) + ' no-customize-support'; 
  2790. </script> 
  2791. <![endif]--> 
  2792. <!--[if gte IE 9]><!--> 
  2793. <script type="text/javascript"> 
  2794. (function() { 
  2795. var request, b = document.body, c = 'className', cs = 'customize-support', rcs = new RegExp('(^|\\s+)(no-)?'+cs+'(\\s+|$)'); 
  2796.  
  2797. <?php if ( $cross_domain ) : ?> 
  2798. request = (function() { var xhr = new XMLHttpRequest(); return ('withCredentials' in xhr); })(); 
  2799. <?php else : ?> 
  2800. request = true; 
  2801. <?php endif; ?> 
  2802.  
  2803. b[c] = b[c].replace( rcs, ' ' ); 
  2804. // The customizer requires postMessage and CORS (if the site is cross domain) 
  2805. b[c] += ( window.postMessage && request ? ' ' : ' no-' ) + cs; 
  2806. }()); 
  2807. </script> 
  2808. <!--<![endif]--> 
  2809. <?php 
  2810.  
  2811. /** 
  2812. * Whether the site is being previewed in the Customizer. 
  2813. * 
  2814. * @since 4.0.0 
  2815. * 
  2816. * @global WP_Customize_Manager $wp_customize Customizer instance. 
  2817. * 
  2818. * @return bool True if the site is being previewed in the Customizer, false otherwise. 
  2819. */ 
  2820. function is_customize_preview() { 
  2821. global $wp_customize; 
  2822.  
  2823. return ( $wp_customize instanceof WP_Customize_Manager ) && $wp_customize->is_preview(); 
.