/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.  
  956. if ( empty( $header->url ) ) { 
  957. return ''; 
  958.  
  959. $width = absint( $header->width ); 
  960. $height = absint( $header->height ); 
  961.  
  962. $attr = wp_parse_args( 
  963. $attr,  
  964. array( 
  965. 'src' => $header->url,  
  966. 'width' => $width,  
  967. 'height' => $height,  
  968. 'alt' => get_bloginfo( 'name' ),  
  969. ); 
  970.  
  971. // Generate 'srcset' and 'sizes' if not already present. 
  972. if ( empty( $attr['srcset'] ) && ! empty( $header->attachment_id ) ) { 
  973. $image_meta = get_post_meta( $header->attachment_id, '_wp_attachment_metadata', true ); 
  974. $size_array = array( $width, $height ); 
  975.  
  976. if ( is_array( $image_meta ) ) { 
  977. $srcset = wp_calculate_image_srcset( $size_array, $header->url, $image_meta, $header->attachment_id ); 
  978. $sizes = ! empty( $attr['sizes'] ) ? $attr['sizes'] : wp_calculate_image_sizes( $size_array, $header->url, $image_meta, $header->attachment_id ); 
  979.  
  980. if ( $srcset && $sizes ) { 
  981. $attr['srcset'] = $srcset; 
  982. $attr['sizes'] = $sizes; 
  983.  
  984. $attr = array_map( 'esc_attr', $attr ); 
  985. $html = '<img'; 
  986.  
  987. foreach ( $attr as $name => $value ) { 
  988. $html .= ' ' . $name . '="' . $value . '"'; 
  989.  
  990. $html .= ' />'; 
  991.  
  992. /** 
  993. * Filters the markup of header images. 
  994. * 
  995. * @since 4.4.0 
  996. * 
  997. * @param string $html The HTML image tag markup being filtered. 
  998. * @param object $header The custom header object returned by 'get_custom_header()'. 
  999. * @param array $attr Array of the attributes for the image tag. 
  1000. */ 
  1001. return apply_filters( 'get_header_image_tag', $html, $header, $attr ); 
  1002.  
  1003. /** 
  1004. * Display the image markup for a custom header image. 
  1005. * 
  1006. * @since 4.4.0 
  1007. * 
  1008. * @param array $attr Optional. Attributes for the image markup. Default empty. 
  1009. */ 
  1010. function the_header_image_tag( $attr = array() ) { 
  1011. echo get_header_image_tag( $attr ); 
  1012.  
  1013. /** 
  1014. * Get random header image data from registered images in theme. 
  1015. * 
  1016. * @since 3.4.0 
  1017. * 
  1018. * @access private 
  1019. * 
  1020. * @global array $_wp_default_headers 
  1021. * @staticvar object $_wp_random_header 
  1022. * 
  1023. * @return object 
  1024. */ 
  1025. function _get_random_header_data() { 
  1026. static $_wp_random_header = null; 
  1027.  
  1028. if ( empty( $_wp_random_header ) ) { 
  1029. global $_wp_default_headers; 
  1030. $header_image_mod = get_theme_mod( 'header_image', '' ); 
  1031. $headers = array(); 
  1032.  
  1033. if ( 'random-uploaded-image' == $header_image_mod ) 
  1034. $headers = get_uploaded_header_images(); 
  1035. elseif ( ! empty( $_wp_default_headers ) ) { 
  1036. if ( 'random-default-image' == $header_image_mod ) { 
  1037. $headers = $_wp_default_headers; 
  1038. } else { 
  1039. if ( current_theme_supports( 'custom-header', 'random-default' ) ) 
  1040. $headers = $_wp_default_headers; 
  1041.  
  1042. if ( empty( $headers ) ) 
  1043. return new stdClass; 
  1044.  
  1045. $_wp_random_header = (object) $headers[ array_rand( $headers ) ]; 
  1046.  
  1047. $_wp_random_header->url = sprintf( $_wp_random_header->url, get_template_directory_uri(), get_stylesheet_directory_uri() ); 
  1048. $_wp_random_header->thumbnail_url = sprintf( $_wp_random_header->thumbnail_url, get_template_directory_uri(), get_stylesheet_directory_uri() ); 
  1049. return $_wp_random_header; 
  1050.  
  1051. /** 
  1052. * Get random header image url from registered images in theme. 
  1053. * 
  1054. * @since 3.2.0 
  1055. * 
  1056. * @return string Path to header image 
  1057. */ 
  1058. function get_random_header_image() { 
  1059. $random_image = _get_random_header_data(); 
  1060. if ( empty( $random_image->url ) ) 
  1061. return ''; 
  1062. return $random_image->url; 
  1063.  
  1064. /** 
  1065. * Check if random header image is in use. 
  1066. * 
  1067. * Always true if user expressly chooses the option in Appearance > Header. 
  1068. * Also true if theme has multiple header images registered, no specific header image 
  1069. * is chosen, and theme turns on random headers with add_theme_support(). 
  1070. * 
  1071. * @since 3.2.0 
  1072. * 
  1073. * @param string $type The random pool to use. any|default|uploaded 
  1074. * @return bool 
  1075. */ 
  1076. function is_random_header_image( $type = 'any' ) { 
  1077. $header_image_mod = get_theme_mod( 'header_image', get_theme_support( 'custom-header', 'default-image' ) ); 
  1078.  
  1079. if ( 'any' == $type ) { 
  1080. if ( 'random-default-image' == $header_image_mod || 'random-uploaded-image' == $header_image_mod || ( '' != get_random_header_image() && empty( $header_image_mod ) ) ) 
  1081. return true; 
  1082. } else { 
  1083. if ( "random-$type-image" == $header_image_mod ) 
  1084. return true; 
  1085. elseif ( 'default' == $type && empty( $header_image_mod ) && '' != get_random_header_image() ) 
  1086. return true; 
  1087.  
  1088. return false; 
  1089.  
  1090. /** 
  1091. * Display header image URL. 
  1092. * 
  1093. * @since 2.1.0 
  1094. */ 
  1095. function header_image() { 
  1096. $image = get_header_image(); 
  1097. if ( $image ) { 
  1098. echo esc_url( $image ); 
  1099.  
  1100. /** 
  1101. * Get the header images uploaded for the current theme. 
  1102. * 
  1103. * @since 3.2.0 
  1104. * 
  1105. * @return array 
  1106. */ 
  1107. function get_uploaded_header_images() { 
  1108. $header_images = array(); 
  1109.  
  1110. // @todo caching 
  1111. $headers = get_posts( array( 'post_type' => 'attachment', 'meta_key' => '_wp_attachment_is_custom_header', 'meta_value' => get_option('stylesheet'), 'orderby' => 'none', 'nopaging' => true ) ); 
  1112.  
  1113. if ( empty( $headers ) ) 
  1114. return array(); 
  1115.  
  1116. foreach ( (array) $headers as $header ) { 
  1117. $url = esc_url_raw( wp_get_attachment_url( $header->ID ) ); 
  1118. $header_data = wp_get_attachment_metadata( $header->ID ); 
  1119. $header_index = $header->ID; 
  1120.  
  1121. $header_images[$header_index] = array(); 
  1122. $header_images[$header_index]['attachment_id'] = $header->ID; 
  1123. $header_images[$header_index]['url'] = $url; 
  1124. $header_images[$header_index]['thumbnail_url'] = $url; 
  1125. $header_images[$header_index]['alt_text'] = get_post_meta( $header->ID, '_wp_attachment_image_alt', true ); 
  1126.  
  1127. if ( isset( $header_data['width'] ) ) 
  1128. $header_images[$header_index]['width'] = $header_data['width']; 
  1129. if ( isset( $header_data['height'] ) ) 
  1130. $header_images[$header_index]['height'] = $header_data['height']; 
  1131.  
  1132. return $header_images; 
  1133.  
  1134. /** 
  1135. * Get the header image data. 
  1136. * 
  1137. * @since 3.4.0 
  1138. * 
  1139. * @global array $_wp_default_headers 
  1140. * 
  1141. * @return object 
  1142. */ 
  1143. function get_custom_header() { 
  1144. global $_wp_default_headers; 
  1145.  
  1146. if ( is_random_header_image() ) { 
  1147. $data = _get_random_header_data(); 
  1148. } else { 
  1149. $data = get_theme_mod( 'header_image_data' ); 
  1150. if ( ! $data && current_theme_supports( 'custom-header', 'default-image' ) ) { 
  1151. $directory_args = array( get_template_directory_uri(), get_stylesheet_directory_uri() ); 
  1152. $data = array(); 
  1153. $data['url'] = $data['thumbnail_url'] = vsprintf( get_theme_support( 'custom-header', 'default-image' ), $directory_args ); 
  1154. if ( ! empty( $_wp_default_headers ) ) { 
  1155. foreach ( (array) $_wp_default_headers as $default_header ) { 
  1156. $url = vsprintf( $default_header['url'], $directory_args ); 
  1157. if ( $data['url'] == $url ) { 
  1158. $data = $default_header; 
  1159. $data['url'] = $url; 
  1160. $data['thumbnail_url'] = vsprintf( $data['thumbnail_url'], $directory_args ); 
  1161. break; 
  1162.  
  1163. $default = array( 
  1164. 'url' => '',  
  1165. 'thumbnail_url' => '',  
  1166. 'width' => get_theme_support( 'custom-header', 'width' ),  
  1167. 'height' => get_theme_support( 'custom-header', 'height' ),  
  1168. ); 
  1169. return (object) wp_parse_args( $data, $default ); 
  1170.  
  1171. /** 
  1172. * Register a selection of default headers to be displayed by the custom header admin UI. 
  1173. * 
  1174. * @since 3.0.0 
  1175. * 
  1176. * @global array $_wp_default_headers 
  1177. * 
  1178. * @param array $headers Array of headers keyed by a string id. The ids point to arrays containing 'url', 'thumbnail_url', and 'description' keys. 
  1179. */ 
  1180. function register_default_headers( $headers ) { 
  1181. global $_wp_default_headers; 
  1182.  
  1183. $_wp_default_headers = array_merge( (array) $_wp_default_headers, (array) $headers ); 
  1184.  
  1185. /** 
  1186. * Unregister default headers. 
  1187. * 
  1188. * This function must be called after register_default_headers() has already added the 
  1189. * header you want to remove. 
  1190. * 
  1191. * @see register_default_headers() 
  1192. * @since 3.0.0 
  1193. * 
  1194. * @global array $_wp_default_headers 
  1195. * 
  1196. * @param string|array $header The header string id (key of array) to remove, or an array thereof. 
  1197. * @return bool|void A single header returns true on success, false on failure. 
  1198. * There is currently no return value for multiple headers. 
  1199. */ 
  1200. function unregister_default_headers( $header ) { 
  1201. global $_wp_default_headers; 
  1202. if ( is_array( $header ) ) { 
  1203. array_map( 'unregister_default_headers', $header ); 
  1204. } elseif ( isset( $_wp_default_headers[ $header ] ) ) { 
  1205. unset( $_wp_default_headers[ $header ] ); 
  1206. return true; 
  1207. } else { 
  1208. return false; 
  1209.  
  1210. /** 
  1211. * Retrieve background image for custom background. 
  1212. * 
  1213. * @since 3.0.0 
  1214. * 
  1215. * @return string 
  1216. */ 
  1217. function get_background_image() { 
  1218. return get_theme_mod('background_image', get_theme_support( 'custom-background', 'default-image' ) ); 
  1219.  
  1220. /** 
  1221. * Display background image path. 
  1222. * 
  1223. * @since 3.0.0 
  1224. */ 
  1225. function background_image() { 
  1226. echo get_background_image(); 
  1227.  
  1228. /** 
  1229. * Retrieve value for custom background color. 
  1230. * 
  1231. * @since 3.0.0 
  1232. * 
  1233. * @return string 
  1234. */ 
  1235. function get_background_color() { 
  1236. return get_theme_mod('background_color', get_theme_support( 'custom-background', 'default-color' ) ); 
  1237.  
  1238. /** 
  1239. * Display background color value. 
  1240. * 
  1241. * @since 3.0.0 
  1242. */ 
  1243. function background_color() { 
  1244. echo get_background_color(); 
  1245.  
  1246. /** 
  1247. * Default custom background callback. 
  1248. * 
  1249. * @since 3.0.0 
  1250. * @access protected 
  1251. */ 
  1252. function _custom_background_cb() { 
  1253. // $background is the saved custom image, or the default image. 
  1254. $background = set_url_scheme( get_background_image() ); 
  1255.  
  1256. // $color is the saved custom color. 
  1257. // A default has to be specified in style.css. It will not be printed here. 
  1258. $color = get_background_color(); 
  1259.  
  1260. if ( $color === get_theme_support( 'custom-background', 'default-color' ) ) { 
  1261. $color = false; 
  1262.  
  1263. if ( ! $background && ! $color ) 
  1264. return; 
  1265.  
  1266. $style = $color ? "background-color: #$color;" : ''; 
  1267.  
  1268. if ( $background ) { 
  1269. $image = " background-image: url('$background');"; 
  1270.  
  1271. $repeat = get_theme_mod( 'background_repeat', get_theme_support( 'custom-background', 'default-repeat' ) ); 
  1272. if ( ! in_array( $repeat, array( 'no-repeat', 'repeat-x', 'repeat-y', 'repeat' ) ) ) 
  1273. $repeat = 'repeat'; 
  1274. $repeat = " background-repeat: $repeat;"; 
  1275.  
  1276. $position = get_theme_mod( 'background_position_x', get_theme_support( 'custom-background', 'default-position-x' ) ); 
  1277. if ( ! in_array( $position, array( 'center', 'right', 'left' ) ) ) 
  1278. $position = 'left'; 
  1279. $position = " background-position: top $position;"; 
  1280.  
  1281. $attachment = get_theme_mod( 'background_attachment', get_theme_support( 'custom-background', 'default-attachment' ) ); 
  1282. if ( ! in_array( $attachment, array( 'fixed', 'scroll' ) ) ) 
  1283. $attachment = 'scroll'; 
  1284. $attachment = " background-attachment: $attachment;"; 
  1285.  
  1286. $style .= $image . $repeat . $position . $attachment; 
  1287. ?> 
  1288. <style type="text/css" id="custom-background-css"> 
  1289. body.custom-background { <?php echo trim( $style ); ?> } 
  1290. </style> 
  1291. <?php 
  1292.  
  1293. /** 
  1294. * Add callback for custom TinyMCE editor stylesheets. 
  1295. * 
  1296. * The parameter $stylesheet is the name of the stylesheet, relative to 
  1297. * the theme root. It also accepts an array of stylesheets. 
  1298. * It is optional and defaults to 'editor-style.css'. 
  1299. * 
  1300. * This function automatically adds another stylesheet with -rtl prefix, e.g. editor-style-rtl.css. 
  1301. * If that file doesn't exist, it is removed before adding the stylesheet(s) to TinyMCE. 
  1302. * If an array of stylesheets is passed to add_editor_style(),  
  1303. * RTL is only added for the first stylesheet. 
  1304. * 
  1305. * Since version 3.4 the TinyMCE body has .rtl CSS class. 
  1306. * It is a better option to use that class and add any RTL styles to the main stylesheet. 
  1307. * 
  1308. * @since 3.0.0 
  1309. * 
  1310. * @global array $editor_styles 
  1311. * 
  1312. * @param array|string $stylesheet Optional. Stylesheet name or array thereof, relative to theme root. 
  1313. * Defaults to 'editor-style.css' 
  1314. */ 
  1315. function add_editor_style( $stylesheet = 'editor-style.css' ) { 
  1316. add_theme_support( 'editor-style' ); 
  1317.  
  1318. if ( ! is_admin() ) 
  1319. return; 
  1320.  
  1321. global $editor_styles; 
  1322. $editor_styles = (array) $editor_styles; 
  1323. $stylesheet = (array) $stylesheet; 
  1324. if ( is_rtl() ) { 
  1325. $rtl_stylesheet = str_replace('.css', '-rtl.css', $stylesheet[0]); 
  1326. $stylesheet[] = $rtl_stylesheet; 
  1327.  
  1328. $editor_styles = array_merge( $editor_styles, $stylesheet ); 
  1329.  
  1330. /** 
  1331. * Removes all visual editor stylesheets. 
  1332. * 
  1333. * @since 3.1.0 
  1334. * 
  1335. * @global array $editor_styles 
  1336. * 
  1337. * @return bool True on success, false if there were no stylesheets to remove. 
  1338. */ 
  1339. function remove_editor_styles() { 
  1340. if ( ! current_theme_supports( 'editor-style' ) ) 
  1341. return false; 
  1342. _remove_theme_support( 'editor-style' ); 
  1343. if ( is_admin() ) 
  1344. $GLOBALS['editor_styles'] = array(); 
  1345. return true; 
  1346.  
  1347. /** 
  1348. * Retrieve any registered editor stylesheets 
  1349. * 
  1350. * @since 4.0.0 
  1351. * 
  1352. * @global array $editor_styles Registered editor stylesheets 
  1353. * 
  1354. * @return array If registered, a list of editor stylesheet URLs. 
  1355. */ 
  1356. function get_editor_stylesheets() { 
  1357. $stylesheets = array(); 
  1358. // load editor_style.css if the current theme supports it 
  1359. if ( ! empty( $GLOBALS['editor_styles'] ) && is_array( $GLOBALS['editor_styles'] ) ) { 
  1360. $editor_styles = $GLOBALS['editor_styles']; 
  1361.  
  1362. $editor_styles = array_unique( array_filter( $editor_styles ) ); 
  1363. $style_uri = get_stylesheet_directory_uri(); 
  1364. $style_dir = get_stylesheet_directory(); 
  1365.  
  1366. // Support externally referenced styles (like, say, fonts). 
  1367. foreach ( $editor_styles as $key => $file ) { 
  1368. if ( preg_match( '~^(https?:)?//~', $file ) ) { 
  1369. $stylesheets[] = esc_url_raw( $file ); 
  1370. unset( $editor_styles[ $key ] ); 
  1371.  
  1372. // Look in a parent theme first, that way child theme CSS overrides. 
  1373. if ( is_child_theme() ) { 
  1374. $template_uri = get_template_directory_uri(); 
  1375. $template_dir = get_template_directory(); 
  1376.  
  1377. foreach ( $editor_styles as $key => $file ) { 
  1378. if ( $file && file_exists( "$template_dir/$file" ) ) { 
  1379. $stylesheets[] = "$template_uri/$file"; 
  1380.  
  1381. foreach ( $editor_styles as $file ) { 
  1382. if ( $file && file_exists( "$style_dir/$file" ) ) { 
  1383. $stylesheets[] = "$style_uri/$file"; 
  1384.  
  1385. /** 
  1386. * Filters the array of stylesheets applied to the editor. 
  1387. * 
  1388. * @since 4.3.0 
  1389. * 
  1390. * @param array $stylesheets Array of stylesheets to be applied to the editor. 
  1391. */ 
  1392. return apply_filters( 'editor_stylesheets', $stylesheets ); 
  1393.  
  1394. /** 
  1395. * Registers theme support for a given feature. 
  1396. * 
  1397. * Must be called in the theme's functions.php file to work. 
  1398. * If attached to a hook, it must be {@see 'after_setup_theme'}. 
  1399. * The {@see 'init'} hook may be too late for some features. 
  1400. * 
  1401. * @since 2.9.0 
  1402. * @since 3.6.0 The `html5` feature was added 
  1403. * @since 3.9.0 The `html5` feature now also accepts 'gallery' and 'caption' 
  1404. * @since 4.1.0 The `title-tag` feature was added 
  1405. * @since 4.5.0 The `customize-selective-refresh-widgets` feature was added 
  1406. * 
  1407. * @global array $_wp_theme_features 
  1408. * 
  1409. * @param string $feature The feature being added. Likely core values include 'post-formats',  
  1410. * 'post-thumbnails', 'html5', 'custom-logo', 'custom-header-uploads',  
  1411. * 'custom-header', 'custom-background', 'title-tag', etc. 
  1412. * @param mixed $args, ... Optional extra arguments to pass along with certain features. 
  1413. * @return void|bool False on failure, void otherwise. 
  1414. */ 
  1415. function add_theme_support( $feature ) { 
  1416. global $_wp_theme_features; 
  1417.  
  1418. if ( func_num_args() == 1 ) 
  1419. $args = true; 
  1420. else 
  1421. $args = array_slice( func_get_args(), 1 ); 
  1422.  
  1423. switch ( $feature ) { 
  1424. case 'post-thumbnails': 
  1425. // All post types are already supported. 
  1426. if ( true === get_theme_support( 'post-thumbnails' ) ) { 
  1427. return; 
  1428.  
  1429. /** 
  1430. * Merge post types with any that already declared their support 
  1431. * for post thumbnails. 
  1432. */ 
  1433. if ( is_array( $args[0] ) && isset( $_wp_theme_features['post-thumbnails'] ) ) { 
  1434. $args[0] = array_unique( array_merge( $_wp_theme_features['post-thumbnails'][0], $args[0] ) ); 
  1435.  
  1436. break; 
  1437.  
  1438. case 'post-formats' : 
  1439. if ( is_array( $args[0] ) ) { 
  1440. $post_formats = get_post_format_slugs(); 
  1441. unset( $post_formats['standard'] ); 
  1442.  
  1443. $args[0] = array_intersect( $args[0], array_keys( $post_formats ) ); 
  1444. break; 
  1445.  
  1446. case 'html5' : 
  1447. // You can't just pass 'html5', you need to pass an array of types. 
  1448. if ( empty( $args[0] ) ) { 
  1449. // Build an array of types for back-compat. 
  1450. $args = array( 0 => array( 'comment-list', 'comment-form', 'search-form' ) ); 
  1451. } elseif ( ! is_array( $args[0] ) ) { 
  1452. _doing_it_wrong( "add_theme_support( 'html5' )", __( 'You need to pass an array of types.' ), '3.6.1' ); 
  1453. return false; 
  1454.  
  1455. // Calling 'html5' again merges, rather than overwrites. 
  1456. if ( isset( $_wp_theme_features['html5'] ) ) 
  1457. $args[0] = array_merge( $_wp_theme_features['html5'][0], $args[0] ); 
  1458. break; 
  1459.  
  1460. case 'custom-logo': 
  1461. if ( ! is_array( $args ) ) { 
  1462. $args = array( 0 => array() ); 
  1463. $defaults = array( 
  1464. 'width' => null,  
  1465. 'height' => null,  
  1466. 'flex-width' => false,  
  1467. 'flex-height' => false,  
  1468. 'header-text' => '',  
  1469. ); 
  1470. $args[0] = wp_parse_args( array_intersect_key( $args[0], $defaults ), $defaults ); 
  1471.  
  1472. // Allow full flexibility if no size is specified. 
  1473. if ( is_null( $args[0]['width'] ) && is_null( $args[0]['height'] ) ) { 
  1474. $args[0]['flex-width'] = true; 
  1475. $args[0]['flex-height'] = true; 
  1476. break; 
  1477.  
  1478. case 'custom-header-uploads' : 
  1479. return add_theme_support( 'custom-header', array( 'uploads' => true ) ); 
  1480.  
  1481. case 'custom-header' : 
  1482. if ( ! is_array( $args ) ) 
  1483. $args = array( 0 => array() ); 
  1484.  
  1485. $defaults = array( 
  1486. 'default-image' => '',  
  1487. 'random-default' => false,  
  1488. 'width' => 0,  
  1489. 'height' => 0,  
  1490. 'flex-height' => false,  
  1491. 'flex-width' => false,  
  1492. 'default-text-color' => '',  
  1493. 'header-text' => true,  
  1494. 'uploads' => true,  
  1495. 'wp-head-callback' => '',  
  1496. 'admin-head-callback' => '',  
  1497. 'admin-preview-callback' => '',  
  1498. ); 
  1499.  
  1500. $jit = isset( $args[0]['__jit'] ); 
  1501. unset( $args[0]['__jit'] ); 
  1502.  
  1503. // Merge in data from previous add_theme_support() calls. 
  1504. // The first value registered wins. (A child theme is set up first.) 
  1505. if ( isset( $_wp_theme_features['custom-header'] ) ) 
  1506. $args[0] = wp_parse_args( $_wp_theme_features['custom-header'][0], $args[0] ); 
  1507.  
  1508. // Load in the defaults at the end, as we need to insure first one wins. 
  1509. // This will cause all constants to be defined, as each arg will then be set to the default. 
  1510. if ( $jit ) 
  1511. $args[0] = wp_parse_args( $args[0], $defaults ); 
  1512.  
  1513. // If a constant was defined, use that value. Otherwise, define the constant to ensure 
  1514. // the constant is always accurate (and is not defined later, overriding our value). 
  1515. // As stated above, the first value wins. 
  1516. // Once we get to wp_loaded (just-in-time), define any constants we haven't already. 
  1517. // Constants are lame. Don't reference them. This is just for backward compatibility. 
  1518.  
  1519. if ( defined( 'NO_HEADER_TEXT' ) ) 
  1520. $args[0]['header-text'] = ! NO_HEADER_TEXT; 
  1521. elseif ( isset( $args[0]['header-text'] ) ) 
  1522. define( 'NO_HEADER_TEXT', empty( $args[0]['header-text'] ) ); 
  1523.  
  1524. if ( defined( 'HEADER_IMAGE_WIDTH' ) ) 
  1525. $args[0]['width'] = (int) HEADER_IMAGE_WIDTH; 
  1526. elseif ( isset( $args[0]['width'] ) ) 
  1527. define( 'HEADER_IMAGE_WIDTH', (int) $args[0]['width'] ); 
  1528.  
  1529. if ( defined( 'HEADER_IMAGE_HEIGHT' ) ) 
  1530. $args[0]['height'] = (int) HEADER_IMAGE_HEIGHT; 
  1531. elseif ( isset( $args[0]['height'] ) ) 
  1532. define( 'HEADER_IMAGE_HEIGHT', (int) $args[0]['height'] ); 
  1533.  
  1534. if ( defined( 'HEADER_TEXTCOLOR' ) ) 
  1535. $args[0]['default-text-color'] = HEADER_TEXTCOLOR; 
  1536. elseif ( isset( $args[0]['default-text-color'] ) ) 
  1537. define( 'HEADER_TEXTCOLOR', $args[0]['default-text-color'] ); 
  1538.  
  1539. if ( defined( 'HEADER_IMAGE' ) ) 
  1540. $args[0]['default-image'] = HEADER_IMAGE; 
  1541. elseif ( isset( $args[0]['default-image'] ) ) 
  1542. define( 'HEADER_IMAGE', $args[0]['default-image'] ); 
  1543.  
  1544. if ( $jit && ! empty( $args[0]['default-image'] ) ) 
  1545. $args[0]['random-default'] = false; 
  1546.  
  1547. // If headers are supported, and we still don't have a defined width or height,  
  1548. // we have implicit flex sizes. 
  1549. if ( $jit ) { 
  1550. if ( empty( $args[0]['width'] ) && empty( $args[0]['flex-width'] ) ) 
  1551. $args[0]['flex-width'] = true; 
  1552. if ( empty( $args[0]['height'] ) && empty( $args[0]['flex-height'] ) ) 
  1553. $args[0]['flex-height'] = true; 
  1554.  
  1555. break; 
  1556.  
  1557. case 'custom-background' : 
  1558. if ( ! is_array( $args ) ) 
  1559. $args = array( 0 => array() ); 
  1560.  
  1561. $defaults = array( 
  1562. 'default-image' => '',  
  1563. 'default-repeat' => 'repeat',  
  1564. 'default-position-x' => 'left',  
  1565. 'default-attachment' => 'scroll',  
  1566. 'default-color' => '',  
  1567. 'wp-head-callback' => '_custom_background_cb',  
  1568. 'admin-head-callback' => '',  
  1569. 'admin-preview-callback' => '',  
  1570. ); 
  1571.  
  1572. $jit = isset( $args[0]['__jit'] ); 
  1573. unset( $args[0]['__jit'] ); 
  1574.  
  1575. // Merge in data from previous add_theme_support() calls. The first value registered wins. 
  1576. if ( isset( $_wp_theme_features['custom-background'] ) ) 
  1577. $args[0] = wp_parse_args( $_wp_theme_features['custom-background'][0], $args[0] ); 
  1578.  
  1579. if ( $jit ) 
  1580. $args[0] = wp_parse_args( $args[0], $defaults ); 
  1581.  
  1582. if ( defined( 'BACKGROUND_COLOR' ) ) 
  1583. $args[0]['default-color'] = BACKGROUND_COLOR; 
  1584. elseif ( isset( $args[0]['default-color'] ) || $jit ) 
  1585. define( 'BACKGROUND_COLOR', $args[0]['default-color'] ); 
  1586.  
  1587. if ( defined( 'BACKGROUND_IMAGE' ) ) 
  1588. $args[0]['default-image'] = BACKGROUND_IMAGE; 
  1589. elseif ( isset( $args[0]['default-image'] ) || $jit ) 
  1590. define( 'BACKGROUND_IMAGE', $args[0]['default-image'] ); 
  1591.  
  1592. break; 
  1593.  
  1594. // Ensure that 'title-tag' is accessible in the admin. 
  1595. case 'title-tag' : 
  1596. // Can be called in functions.php but must happen before wp_loaded, i.e. not in header.php. 
  1597. if ( did_action( 'wp_loaded' ) ) { 
  1598. /** translators: 1: Theme support 2: hook name */ 
  1599. _doing_it_wrong( "add_theme_support( 'title-tag' )", sprintf( __( 'Theme support for %1$s should be registered before the %2$s hook.' ),  
  1600. '<code>title-tag</code>', '<code>wp_loaded</code>' ), '4.1.0' ); 
  1601.  
  1602. return false; 
  1603.  
  1604. $_wp_theme_features[ $feature ] = $args; 
  1605.  
  1606. /** 
  1607. * Registers the internal custom header and background routines. 
  1608. * 
  1609. * @since 3.4.0 
  1610. * @access private 
  1611. * 
  1612. * @global Custom_Image_Header $custom_image_header 
  1613. * @global Custom_Background $custom_background 
  1614. */ 
  1615. function _custom_header_background_just_in_time() { 
  1616. global $custom_image_header, $custom_background; 
  1617.  
  1618. if ( current_theme_supports( 'custom-header' ) ) { 
  1619. // In case any constants were defined after an add_custom_image_header() call, re-run. 
  1620. add_theme_support( 'custom-header', array( '__jit' => true ) ); 
  1621.  
  1622. $args = get_theme_support( 'custom-header' ); 
  1623. if ( $args[0]['wp-head-callback'] ) 
  1624. add_action( 'wp_head', $args[0]['wp-head-callback'] ); 
  1625.  
  1626. if ( is_admin() ) { 
  1627. require_once( ABSPATH . 'wp-admin/custom-header.php' ); 
  1628. $custom_image_header = new Custom_Image_Header( $args[0]['admin-head-callback'], $args[0]['admin-preview-callback'] ); 
  1629.  
  1630. if ( current_theme_supports( 'custom-background' ) ) { 
  1631. // In case any constants were defined after an add_custom_background() call, re-run. 
  1632. add_theme_support( 'custom-background', array( '__jit' => true ) ); 
  1633.  
  1634. $args = get_theme_support( 'custom-background' ); 
  1635. add_action( 'wp_head', $args[0]['wp-head-callback'] ); 
  1636.  
  1637. if ( is_admin() ) { 
  1638. require_once( ABSPATH . 'wp-admin/custom-background.php' ); 
  1639. $custom_background = new Custom_Background( $args[0]['admin-head-callback'], $args[0]['admin-preview-callback'] ); 
  1640.  
  1641. /** 
  1642. * Adds CSS to hide header text for custom logo, based on Customizer setting. 
  1643. * 
  1644. * @since 4.5.0 
  1645. * @access private 
  1646. */ 
  1647. function _custom_logo_header_styles() { 
  1648. if ( ! current_theme_supports( 'custom-header', 'header-text' ) && get_theme_support( 'custom-logo', 'header-text' ) && ! get_theme_mod( 'header_text', true ) ) { 
  1649. $classes = (array) get_theme_support( 'custom-logo', 'header-text' ); 
  1650. $classes = array_map( 'sanitize_html_class', $classes ); 
  1651. $classes = '.' . implode( ', .', $classes ); 
  1652.  
  1653. ?> 
  1654. <!-- Custom Logo: hide header text --> 
  1655. <style id="custom-logo-css" type="text/css"> 
  1656. <?php echo $classes; ?> { 
  1657. position: absolute; 
  1658. clip: rect(1px, 1px, 1px, 1px); 
  1659. </style> 
  1660. <?php 
  1661.  
  1662. /** 
  1663. * Gets the theme support arguments passed when registering that support 
  1664. * 
  1665. * @since 3.1.0 
  1666. * 
  1667. * @global array $_wp_theme_features 
  1668. * 
  1669. * @param string $feature the feature to check 
  1670. * @return mixed The array of extra arguments or the value for the registered feature. 
  1671. */ 
  1672. function get_theme_support( $feature ) { 
  1673. global $_wp_theme_features; 
  1674. if ( ! isset( $_wp_theme_features[ $feature ] ) ) 
  1675. return false; 
  1676.  
  1677. if ( func_num_args() <= 1 ) 
  1678. return $_wp_theme_features[ $feature ]; 
  1679.  
  1680. $args = array_slice( func_get_args(), 1 ); 
  1681. switch ( $feature ) { 
  1682. case 'custom-logo' : 
  1683. case 'custom-header' : 
  1684. case 'custom-background' : 
  1685. if ( isset( $_wp_theme_features[ $feature ][0][ $args[0] ] ) ) 
  1686. return $_wp_theme_features[ $feature ][0][ $args[0] ]; 
  1687. return false; 
  1688.  
  1689. default : 
  1690. return $_wp_theme_features[ $feature ]; 
  1691.  
  1692. /** 
  1693. * Allows a theme to de-register its support of a certain feature 
  1694. * 
  1695. * Should be called in the theme's functions.php file. Generally would 
  1696. * be used for child themes to override support from the parent theme. 
  1697. * 
  1698. * @since 3.0.0 
  1699. * @see add_theme_support() 
  1700. * @param string $feature the feature being added 
  1701. * @return bool|void Whether feature was removed. 
  1702. */ 
  1703. function remove_theme_support( $feature ) { 
  1704. // Blacklist: for internal registrations not used directly by themes. 
  1705. if ( in_array( $feature, array( 'editor-style', 'widgets', 'menus' ) ) ) 
  1706. return false; 
  1707.  
  1708. return _remove_theme_support( $feature ); 
  1709.  
  1710. /** 
  1711. * Do not use. Removes theme support internally, ignorant of the blacklist. 
  1712. * 
  1713. * @access private 
  1714. * @since 3.1.0 
  1715. * 
  1716. * @global array $_wp_theme_features 
  1717. * @global Custom_Image_Header $custom_image_header 
  1718. * @global Custom_Background $custom_background 
  1719. * 
  1720. * @param string $feature 
  1721. */ 
  1722. function _remove_theme_support( $feature ) { 
  1723. global $_wp_theme_features; 
  1724.  
  1725. switch ( $feature ) { 
  1726. case 'custom-header-uploads' : 
  1727. if ( ! isset( $_wp_theme_features['custom-header'] ) ) 
  1728. return false; 
  1729. add_theme_support( 'custom-header', array( 'uploads' => false ) ); 
  1730. return; // Do not continue - custom-header-uploads no longer exists. 
  1731.  
  1732. if ( ! isset( $_wp_theme_features[ $feature ] ) ) 
  1733. return false; 
  1734.  
  1735. switch ( $feature ) { 
  1736. case 'custom-header' : 
  1737. if ( ! did_action( 'wp_loaded' ) ) 
  1738. break; 
  1739. $support = get_theme_support( 'custom-header' ); 
  1740. if ( $support[0]['wp-head-callback'] ) 
  1741. remove_action( 'wp_head', $support[0]['wp-head-callback'] ); 
  1742. remove_action( 'admin_menu', array( $GLOBALS['custom_image_header'], 'init' ) ); 
  1743. unset( $GLOBALS['custom_image_header'] ); 
  1744. break; 
  1745.  
  1746. case 'custom-background' : 
  1747. if ( ! did_action( 'wp_loaded' ) ) 
  1748. break; 
  1749. $support = get_theme_support( 'custom-background' ); 
  1750. remove_action( 'wp_head', $support[0]['wp-head-callback'] ); 
  1751. remove_action( 'admin_menu', array( $GLOBALS['custom_background'], 'init' ) ); 
  1752. unset( $GLOBALS['custom_background'] ); 
  1753. break; 
  1754.  
  1755. unset( $_wp_theme_features[ $feature ] ); 
  1756. return true; 
  1757.  
  1758. /** 
  1759. * Checks a theme's support for a given feature 
  1760. * 
  1761. * @since 2.9.0 
  1762. * 
  1763. * @global array $_wp_theme_features 
  1764. * 
  1765. * @param string $feature the feature being checked 
  1766. * @return bool 
  1767. */ 
  1768. function current_theme_supports( $feature ) { 
  1769. global $_wp_theme_features; 
  1770.  
  1771. if ( 'custom-header-uploads' == $feature ) 
  1772. return current_theme_supports( 'custom-header', 'uploads' ); 
  1773.  
  1774. if ( !isset( $_wp_theme_features[$feature] ) ) 
  1775. return false; 
  1776.  
  1777. // If no args passed then no extra checks need be performed 
  1778. if ( func_num_args() <= 1 ) 
  1779. return true; 
  1780.  
  1781. $args = array_slice( func_get_args(), 1 ); 
  1782.  
  1783. switch ( $feature ) { 
  1784. case 'post-thumbnails': 
  1785. // post-thumbnails can be registered for only certain content/post types by passing 
  1786. // an array of types to add_theme_support(). If no array was passed, then 
  1787. // any type is accepted 
  1788. if ( true === $_wp_theme_features[$feature] ) // Registered for all types 
  1789. return true; 
  1790. $content_type = $args[0]; 
  1791. return in_array( $content_type, $_wp_theme_features[$feature][0] ); 
  1792.  
  1793. case 'html5': 
  1794. case 'post-formats': 
  1795. // specific post formats can be registered by passing an array of types to 
  1796. // add_theme_support() 
  1797.  
  1798. // Specific areas of HTML5 support *must* be passed via an array to add_theme_support() 
  1799.  
  1800. $type = $args[0]; 
  1801. return in_array( $type, $_wp_theme_features[$feature][0] ); 
  1802.  
  1803. case 'custom-logo': 
  1804. case 'custom-header': 
  1805. case 'custom-background': 
  1806. // Specific capabilities can be registered by passing an array to add_theme_support(). 
  1807. return ( isset( $_wp_theme_features[ $feature ][0][ $args[0] ] ) && $_wp_theme_features[ $feature ][0][ $args[0] ] ); 
  1808.  
  1809. /** 
  1810. * Filters whether the current theme supports a specific feature. 
  1811. * 
  1812. * The dynamic portion of the hook name, `$feature`, refers to the specific theme 
  1813. * feature. Possible values include 'post-formats', 'post-thumbnails', 'custom-background',  
  1814. * 'custom-header', 'menus', 'automatic-feed-links', 'html5', and `customize-selective-refresh-widgets`. 
  1815. * 
  1816. * @since 3.4.0 
  1817. * 
  1818. * @param bool true Whether the current theme supports the given feature. Default true. 
  1819. * @param array $args Array of arguments for the feature. 
  1820. * @param string $feature The theme feature. 
  1821. */ 
  1822. return apply_filters( "current_theme_supports-{$feature}", true, $args, $_wp_theme_features[$feature] ); 
  1823.  
  1824. /** 
  1825. * Checks a theme's support for a given feature before loading the functions which implement it. 
  1826. * 
  1827. * @since 2.9.0 
  1828. * 
  1829. * @param string $feature The feature being checked. 
  1830. * @param string $include Path to the file. 
  1831. * @return bool True if the current theme supports the supplied feature, false otherwise. 
  1832. */ 
  1833. function require_if_theme_supports( $feature, $include ) { 
  1834. if ( current_theme_supports( $feature ) ) { 
  1835. require ( $include ); 
  1836. return true; 
  1837. return false; 
  1838.  
  1839. /** 
  1840. * Checks an attachment being deleted to see if it's a header or background image. 
  1841. * 
  1842. * If true it removes the theme modification which would be pointing at the deleted 
  1843. * attachment. 
  1844. * 
  1845. * @access private 
  1846. * @since 3.0.0 
  1847. * @since 4.3.0 Also removes `header_image_data`. 
  1848. * @since 4.5.0 Also removes custom logo theme mods. 
  1849. * 
  1850. * @param int $id The attachment id. 
  1851. */ 
  1852. function _delete_attachment_theme_mod( $id ) { 
  1853. $attachment_image = wp_get_attachment_url( $id ); 
  1854. $header_image = get_header_image(); 
  1855. $background_image = get_background_image(); 
  1856. $custom_logo_id = get_theme_mod( 'custom_logo' ); 
  1857.  
  1858. if ( $custom_logo_id && $custom_logo_id == $id ) { 
  1859. remove_theme_mod( 'custom_logo' ); 
  1860. remove_theme_mod( 'header_text' ); 
  1861.  
  1862. if ( $header_image && $header_image == $attachment_image ) { 
  1863. remove_theme_mod( 'header_image' ); 
  1864. remove_theme_mod( 'header_image_data' ); 
  1865.  
  1866. if ( $background_image && $background_image == $attachment_image ) { 
  1867. remove_theme_mod( 'background_image' ); 
  1868.  
  1869. /** 
  1870. * Checks if a theme has been changed and runs 'after_switch_theme' hook on the next WP load. 
  1871. * 
  1872. * See {@see 'after_switch_theme'}. 
  1873. * 
  1874. * @since 3.3.0 
  1875. */ 
  1876. function check_theme_switched() { 
  1877. if ( $stylesheet = get_option( 'theme_switched' ) ) { 
  1878. $old_theme = wp_get_theme( $stylesheet ); 
  1879.  
  1880. // Prevent retrieve_widgets() from running since Customizer already called it up front 
  1881. if ( get_option( 'theme_switched_via_customizer' ) ) { 
  1882. remove_action( 'after_switch_theme', '_wp_sidebars_changed' ); 
  1883. update_option( 'theme_switched_via_customizer', false ); 
  1884.  
  1885. if ( $old_theme->exists() ) { 
  1886. /** 
  1887. * Fires on the first WP load after a theme switch if the old theme still exists. 
  1888. * 
  1889. * This action fires multiple times and the parameters differs 
  1890. * according to the context, if the old theme exists or not. 
  1891. * If the old theme is missing, the parameter will be the slug 
  1892. * of the old theme. 
  1893. * 
  1894. * @since 3.3.0 
  1895. * 
  1896. * @param string $old_name Old theme name. 
  1897. * @param WP_Theme $old_theme WP_Theme instance of the old theme. 
  1898. */ 
  1899. do_action( 'after_switch_theme', $old_theme->get( 'Name' ), $old_theme ); 
  1900. } else { 
  1901. /** This action is documented in wp-includes/theme.php */ 
  1902. do_action( 'after_switch_theme', $stylesheet ); 
  1903. flush_rewrite_rules(); 
  1904.  
  1905. update_option( 'theme_switched', false ); 
  1906.  
  1907. /** 
  1908. * Includes and instantiates the WP_Customize_Manager class. 
  1909. * 
  1910. * Loads the Customizer at plugins_loaded when accessing the customize.php admin 
  1911. * page or when any request includes a wp_customize=on param, either as a GET 
  1912. * query var or as POST data. This param is a signal for whether to bootstrap 
  1913. * the Customizer when WordPress is loading, especially in the Customizer preview 
  1914. * or when making Customizer Ajax requests for widgets or menus. 
  1915. * 
  1916. * @since 3.4.0 
  1917. * 
  1918. * @global WP_Customize_Manager $wp_customize 
  1919. */ 
  1920. function _wp_customize_include() { 
  1921. if ( ! ( ( isset( $_REQUEST['wp_customize'] ) && 'on' == $_REQUEST['wp_customize'] ) 
  1922. || ( is_admin() && 'customize.php' == basename( $_SERVER['PHP_SELF'] ) ) 
  1923. ) ) { 
  1924. return; 
  1925.  
  1926. require_once ABSPATH . WPINC . '/class-wp-customize-manager.php'; 
  1927. $GLOBALS['wp_customize'] = new WP_Customize_Manager(); 
  1928.  
  1929. /** 
  1930. * Adds settings for the customize-loader script. 
  1931. * 
  1932. * @since 3.4.0 
  1933. */ 
  1934. function _wp_customize_loader_settings() { 
  1935. $admin_origin = parse_url( admin_url() ); 
  1936. $home_origin = parse_url( home_url() ); 
  1937. $cross_domain = ( strtolower( $admin_origin[ 'host' ] ) != strtolower( $home_origin[ 'host' ] ) ); 
  1938.  
  1939. $browser = array( 
  1940. 'mobile' => wp_is_mobile(),  
  1941. 'ios' => wp_is_mobile() && preg_match( '/iPad|iPod|iPhone/', $_SERVER['HTTP_USER_AGENT'] ),  
  1942. ); 
  1943.  
  1944. $settings = array( 
  1945. 'url' => esc_url( admin_url( 'customize.php' ) ),  
  1946. 'isCrossDomain' => $cross_domain,  
  1947. 'browser' => $browser,  
  1948. 'l10n' => array( 
  1949. 'saveAlert' => __( 'The changes you made will be lost if you navigate away from this page.' ),  
  1950. 'mainIframeTitle' => __( 'Customizer' ),  
  1951. ),  
  1952. ); 
  1953.  
  1954. $script = 'var _wpCustomizeLoaderSettings = ' . wp_json_encode( $settings ) . ';'; 
  1955.  
  1956. $wp_scripts = wp_scripts(); 
  1957. $data = $wp_scripts->get_data( 'customize-loader', 'data' ); 
  1958. if ( $data ) 
  1959. $script = "$data\n$script"; 
  1960.  
  1961. $wp_scripts->add_data( 'customize-loader', 'data', $script ); 
  1962.  
  1963. /** 
  1964. * Returns a URL to load the Customizer. 
  1965. * 
  1966. * @since 3.4.0 
  1967. * 
  1968. * @param string $stylesheet Optional. Theme to customize. Defaults to current theme. 
  1969. * The theme's stylesheet will be urlencoded if necessary. 
  1970. * @return string 
  1971. */ 
  1972. function wp_customize_url( $stylesheet = null ) { 
  1973. $url = admin_url( 'customize.php' ); 
  1974. if ( $stylesheet ) 
  1975. $url .= '?theme=' . urlencode( $stylesheet ); 
  1976. return esc_url( $url ); 
  1977.  
  1978. /** 
  1979. * Prints a script to check whether or not the Customizer is supported,  
  1980. * and apply either the no-customize-support or customize-support class 
  1981. * to the body. 
  1982. * 
  1983. * This function MUST be called inside the body tag. 
  1984. * 
  1985. * Ideally, call this function immediately after the body tag is opened. 
  1986. * This prevents a flash of unstyled content. 
  1987. * 
  1988. * It is also recommended that you add the "no-customize-support" class 
  1989. * to the body tag by default. 
  1990. * 
  1991. * @since 3.4.0 
  1992. */ 
  1993. function wp_customize_support_script() { 
  1994. $admin_origin = parse_url( admin_url() ); 
  1995. $home_origin = parse_url( home_url() ); 
  1996. $cross_domain = ( strtolower( $admin_origin[ 'host' ] ) != strtolower( $home_origin[ 'host' ] ) ); 
  1997.  
  1998. ?> 
  1999. <script type="text/javascript"> 
  2000. (function() { 
  2001. var request, b = document.body, c = 'className', cs = 'customize-support', rcs = new RegExp('(^|\\s+)(no-)?'+cs+'(\\s+|$)'); 
  2002.  
  2003. <?php if ( $cross_domain ): ?> 
  2004. request = (function() { var xhr = new XMLHttpRequest(); return ('withCredentials' in xhr); })(); 
  2005. <?php else: ?> 
  2006. request = true; 
  2007. <?php endif; ?> 
  2008.  
  2009. b[c] = b[c].replace( rcs, ' ' ); 
  2010. b[c] += ( window.postMessage && request ? ' ' : ' no-' ) + cs; 
  2011. }()); 
  2012. </script> 
  2013. <?php 
  2014.  
  2015. /** 
  2016. * Whether the site is being previewed in the Customizer. 
  2017. * 
  2018. * @since 4.0.0 
  2019. * 
  2020. * @global WP_Customize_Manager $wp_customize Customizer instance. 
  2021. * 
  2022. * @return bool True if the site is being previewed in the Customizer, false otherwise. 
  2023. */ 
  2024. function is_customize_preview() { 
  2025. global $wp_customize; 
  2026.  
  2027. return ( $wp_customize instanceof WP_Customize_Manager ) && $wp_customize->is_preview(); 
.