PageRenderTime 62ms CodeModel.GetById 21ms RepoModel.GetById 0ms app.codeStats 1ms

/htdocs/wp-includes/theme.php

https://gitlab.com/VTTE/sitios-vtte
PHP | 1716 lines | 808 code | 194 blank | 714 comment | 185 complexity | a30ae245f6e5392b643484eda67ccdc4 MD5 | raw file
  1. <?php
  2. /**
  3. * Theme, template, and stylesheet functions.
  4. *
  5. * @package WordPress
  6. * @subpackage Theme
  7. */
  8. /**
  9. * Returns an array of WP_Theme objects based on the arguments.
  10. *
  11. * Despite advances over get_themes(), this function is quite expensive, and grows
  12. * linearly with additional themes. Stick to wp_get_theme() if possible.
  13. *
  14. * @since 3.4.0
  15. *
  16. * @global array $wp_theme_directories
  17. * @staticvar array $_themes
  18. *
  19. * @param array $args {
  20. * Optional. The search arguments.
  21. *
  22. * @type mixed $errors True to return themes with errors, false to return themes without errors, null to return all themes.
  23. * Defaults to false.
  24. * @type mixed $allowed (Multisite) True to return only allowed themes for a site. False to return only disallowed themes for a site.
  25. * 'site' to return only site-allowed themes. 'network' to return only network-allowed themes.
  26. * Null to return all themes. Defaults to null.
  27. * @type int $blog_id (Multisite) The blog ID used to calculate which themes are allowed.
  28. * Defaults to 0, synonymous for the current blog.
  29. * }
  30. * @return WP_Theme[] Array of WP_Theme objects.
  31. */
  32. function wp_get_themes( $args = array() ) {
  33. global $wp_theme_directories;
  34. $defaults = array(
  35. 'errors' => false,
  36. 'allowed' => null,
  37. 'blog_id' => 0,
  38. );
  39. $args = wp_parse_args( $args, $defaults );
  40. $theme_directories = search_theme_directories();
  41. if ( is_array( $wp_theme_directories ) && count( $wp_theme_directories ) > 1 ) {
  42. // Make sure the current theme wins out, in case search_theme_directories() picks the wrong
  43. // one in the case of a conflict. (Normally, last registered theme root wins.)
  44. $current_theme = get_stylesheet();
  45. if ( isset( $theme_directories[ $current_theme ] ) ) {
  46. $root_of_current_theme = get_raw_theme_root( $current_theme );
  47. if ( ! in_array( $root_of_current_theme, $wp_theme_directories ) ) {
  48. $root_of_current_theme = WP_CONTENT_DIR . $root_of_current_theme;
  49. }
  50. $theme_directories[ $current_theme ]['theme_root'] = $root_of_current_theme;
  51. }
  52. }
  53. if ( empty( $theme_directories ) ) {
  54. return array();
  55. }
  56. if ( is_multisite() && null !== $args['allowed'] ) {
  57. $allowed = $args['allowed'];
  58. if ( 'network' === $allowed ) {
  59. $theme_directories = array_intersect_key( $theme_directories, WP_Theme::get_allowed_on_network() );
  60. } elseif ( 'site' === $allowed ) {
  61. $theme_directories = array_intersect_key( $theme_directories, WP_Theme::get_allowed_on_site( $args['blog_id'] ) );
  62. } elseif ( $allowed ) {
  63. $theme_directories = array_intersect_key( $theme_directories, WP_Theme::get_allowed( $args['blog_id'] ) );
  64. } else {
  65. $theme_directories = array_diff_key( $theme_directories, WP_Theme::get_allowed( $args['blog_id'] ) );
  66. }
  67. }
  68. $themes = array();
  69. static $_themes = array();
  70. foreach ( $theme_directories as $theme => $theme_root ) {
  71. if ( isset( $_themes[ $theme_root['theme_root'] . '/' . $theme ] ) ) {
  72. $themes[ $theme ] = $_themes[ $theme_root['theme_root'] . '/' . $theme ];
  73. } else {
  74. $themes[ $theme ] = new WP_Theme( $theme, $theme_root['theme_root'] );
  75. $_themes[ $theme_root['theme_root'] . '/' . $theme ] = $themes[ $theme ];
  76. }
  77. }
  78. if ( null !== $args['errors'] ) {
  79. foreach ( $themes as $theme => $wp_theme ) {
  80. if ( $wp_theme->errors() != $args['errors'] ) {
  81. unset( $themes[ $theme ] );
  82. }
  83. }
  84. }
  85. return $themes;
  86. }
  87. /**
  88. * Gets a WP_Theme object for a theme.
  89. *
  90. * @since 3.4.0
  91. *
  92. * @global array $wp_theme_directories
  93. *
  94. * @param string $stylesheet Directory name for the theme. Optional. Defaults to current theme.
  95. * @param string $theme_root Absolute path of the theme root to look in. Optional. If not specified, get_raw_theme_root()
  96. * is used to calculate the theme root for the $stylesheet provided (or current theme).
  97. * @return WP_Theme Theme object. Be sure to check the object's exists() method if you need to confirm the theme's existence.
  98. */
  99. function wp_get_theme( $stylesheet = '', $theme_root = '' ) {
  100. global $wp_theme_directories;
  101. if ( empty( $stylesheet ) ) {
  102. $stylesheet = get_stylesheet();
  103. }
  104. if ( empty( $theme_root ) ) {
  105. $theme_root = get_raw_theme_root( $stylesheet );
  106. if ( false === $theme_root ) {
  107. $theme_root = WP_CONTENT_DIR . '/themes';
  108. } elseif ( ! in_array( $theme_root, (array) $wp_theme_directories ) ) {
  109. $theme_root = WP_CONTENT_DIR . $theme_root;
  110. }
  111. }
  112. return new WP_Theme( $stylesheet, $theme_root );
  113. }
  114. /**
  115. * Clears the cache held by get_theme_roots() and WP_Theme.
  116. *
  117. * @since 3.5.0
  118. * @param bool $clear_update_cache Whether to clear the Theme updates cache
  119. */
  120. function wp_clean_themes_cache( $clear_update_cache = true ) {
  121. if ( $clear_update_cache ) {
  122. delete_site_transient( 'update_themes' );
  123. }
  124. search_theme_directories( true );
  125. foreach ( wp_get_themes( array( 'errors' => null ) ) as $theme ) {
  126. $theme->cache_delete();
  127. }
  128. }
  129. /**
  130. * Whether a child theme is in use.
  131. *
  132. * @since 3.0.0
  133. *
  134. * @return bool true if a child theme is in use, false otherwise.
  135. */
  136. function is_child_theme() {
  137. return ( TEMPLATEPATH !== STYLESHEETPATH );
  138. }
  139. /**
  140. * Retrieve name of the current stylesheet.
  141. *
  142. * The theme name that the administrator has currently set the front end theme
  143. * as.
  144. *
  145. * For all intents and purposes, the template name and the stylesheet name are
  146. * going to be the same for most cases.
  147. *
  148. * @since 1.5.0
  149. *
  150. * @return string Stylesheet name.
  151. */
  152. function get_stylesheet() {
  153. /**
  154. * Filters the name of current stylesheet.
  155. *
  156. * @since 1.5.0
  157. *
  158. * @param string $stylesheet Name of the current stylesheet.
  159. */
  160. return apply_filters( 'stylesheet', get_option( 'stylesheet' ) );
  161. }
  162. /**
  163. * Retrieve stylesheet directory path for current theme.
  164. *
  165. * @since 1.5.0
  166. *
  167. * @return string Path to current theme directory.
  168. */
  169. function get_stylesheet_directory() {
  170. $stylesheet = get_stylesheet();
  171. $theme_root = get_theme_root( $stylesheet );
  172. $stylesheet_dir = "$theme_root/$stylesheet";
  173. /**
  174. * Filters the stylesheet directory path for current theme.
  175. *
  176. * @since 1.5.0
  177. *
  178. * @param string $stylesheet_dir Absolute path to the current theme.
  179. * @param string $stylesheet Directory name of the current theme.
  180. * @param string $theme_root Absolute path to themes directory.
  181. */
  182. return apply_filters( 'stylesheet_directory', $stylesheet_dir, $stylesheet, $theme_root );
  183. }
  184. /**
  185. * Retrieve stylesheet directory URI.
  186. *
  187. * @since 1.5.0
  188. *
  189. * @return string
  190. */
  191. function get_stylesheet_directory_uri() {
  192. $stylesheet = str_replace( '%2F', '/', rawurlencode( get_stylesheet() ) );
  193. $theme_root_uri = get_theme_root_uri( $stylesheet );
  194. $stylesheet_dir_uri = "$theme_root_uri/$stylesheet";
  195. /**
  196. * Filters the stylesheet directory URI.
  197. *
  198. * @since 1.5.0
  199. *
  200. * @param string $stylesheet_dir_uri Stylesheet directory URI.
  201. * @param string $stylesheet Name of the activated theme's directory.
  202. * @param string $theme_root_uri Themes root URI.
  203. */
  204. return apply_filters( 'stylesheet_directory_uri', $stylesheet_dir_uri, $stylesheet, $theme_root_uri );
  205. }
  206. /**
  207. * Retrieves the URI of current theme stylesheet.
  208. *
  209. * The stylesheet file name is 'style.css' which is appended to the stylesheet directory URI path.
  210. * See get_stylesheet_directory_uri().
  211. *
  212. * @since 1.5.0
  213. *
  214. * @return string
  215. */
  216. function get_stylesheet_uri() {
  217. $stylesheet_dir_uri = get_stylesheet_directory_uri();
  218. $stylesheet_uri = $stylesheet_dir_uri . '/style.css';
  219. /**
  220. * Filters the URI of the current theme stylesheet.
  221. *
  222. * @since 1.5.0
  223. *
  224. * @param string $stylesheet_uri Stylesheet URI for the current theme/child theme.
  225. * @param string $stylesheet_dir_uri Stylesheet directory URI for the current theme/child theme.
  226. */
  227. return apply_filters( 'stylesheet_uri', $stylesheet_uri, $stylesheet_dir_uri );
  228. }
  229. /**
  230. * Retrieves the localized stylesheet URI.
  231. *
  232. * The stylesheet directory for the localized stylesheet files are located, by
  233. * default, in the base theme directory. The name of the locale file will be the
  234. * locale followed by '.css'. If that does not exist, then the text direction
  235. * stylesheet will be checked for existence, for example 'ltr.css'.
  236. *
  237. * The theme may change the location of the stylesheet directory by either using
  238. * the {@see 'stylesheet_directory_uri'} or {@see 'locale_stylesheet_uri'} filters.
  239. *
  240. * If you want to change the location of the stylesheet files for the entire
  241. * WordPress workflow, then change the former. If you just have the locale in a
  242. * separate folder, then change the latter.
  243. *
  244. * @since 2.1.0
  245. *
  246. * @global WP_Locale $wp_locale WordPress date and time locale object.
  247. *
  248. * @return string
  249. */
  250. function get_locale_stylesheet_uri() {
  251. global $wp_locale;
  252. $stylesheet_dir_uri = get_stylesheet_directory_uri();
  253. $dir = get_stylesheet_directory();
  254. $locale = get_locale();
  255. if ( file_exists( "$dir/$locale.css" ) ) {
  256. $stylesheet_uri = "$stylesheet_dir_uri/$locale.css";
  257. } elseif ( ! empty( $wp_locale->text_direction ) && file_exists( "$dir/{$wp_locale->text_direction}.css" ) ) {
  258. $stylesheet_uri = "$stylesheet_dir_uri/{$wp_locale->text_direction}.css";
  259. } else {
  260. $stylesheet_uri = '';
  261. }
  262. /**
  263. * Filters the localized stylesheet URI.
  264. *
  265. * @since 2.1.0
  266. *
  267. * @param string $stylesheet_uri Localized stylesheet URI.
  268. * @param string $stylesheet_dir_uri Stylesheet directory URI.
  269. */
  270. return apply_filters( 'locale_stylesheet_uri', $stylesheet_uri, $stylesheet_dir_uri );
  271. }
  272. /**
  273. * Retrieve name of the current theme.
  274. *
  275. * @since 1.5.0
  276. *
  277. * @return string Template name.
  278. */
  279. function get_template() {
  280. /**
  281. * Filters the name of the current theme.
  282. *
  283. * @since 1.5.0
  284. *
  285. * @param string $template Current theme's directory name.
  286. */
  287. return apply_filters( 'template', get_option( 'template' ) );
  288. }
  289. /**
  290. * Retrieve current theme directory.
  291. *
  292. * @since 1.5.0
  293. *
  294. * @return string Template directory path.
  295. */
  296. function get_template_directory() {
  297. $template = get_template();
  298. $theme_root = get_theme_root( $template );
  299. $template_dir = "$theme_root/$template";
  300. /**
  301. * Filters the current theme directory path.
  302. *
  303. * @since 1.5.0
  304. *
  305. * @param string $template_dir The URI of the current theme directory.
  306. * @param string $template Directory name of the current theme.
  307. * @param string $theme_root Absolute path to the themes directory.
  308. */
  309. return apply_filters( 'template_directory', $template_dir, $template, $theme_root );
  310. }
  311. /**
  312. * Retrieve theme directory URI.
  313. *
  314. * @since 1.5.0
  315. *
  316. * @return string Template directory URI.
  317. */
  318. function get_template_directory_uri() {
  319. $template = str_replace( '%2F', '/', rawurlencode( get_template() ) );
  320. $theme_root_uri = get_theme_root_uri( $template );
  321. $template_dir_uri = "$theme_root_uri/$template";
  322. /**
  323. * Filters the current theme directory URI.
  324. *
  325. * @since 1.5.0
  326. *
  327. * @param string $template_dir_uri The URI of the current theme directory.
  328. * @param string $template Directory name of the current theme.
  329. * @param string $theme_root_uri The themes root URI.
  330. */
  331. return apply_filters( 'template_directory_uri', $template_dir_uri, $template, $theme_root_uri );
  332. }
  333. /**
  334. * Retrieve theme roots.
  335. *
  336. * @since 2.9.0
  337. *
  338. * @global array $wp_theme_directories
  339. *
  340. * @return array|string An array of theme roots keyed by template/stylesheet or a single theme root if all themes have the same root.
  341. */
  342. function get_theme_roots() {
  343. global $wp_theme_directories;
  344. if ( ! is_array( $wp_theme_directories ) || count( $wp_theme_directories ) <= 1 ) {
  345. return '/themes';
  346. }
  347. $theme_roots = get_site_transient( 'theme_roots' );
  348. if ( false === $theme_roots ) {
  349. search_theme_directories( true ); // Regenerate the transient.
  350. $theme_roots = get_site_transient( 'theme_roots' );
  351. }
  352. return $theme_roots;
  353. }
  354. /**
  355. * Register a directory that contains themes.
  356. *
  357. * @since 2.9.0
  358. *
  359. * @global array $wp_theme_directories
  360. *
  361. * @param string $directory Either the full filesystem path to a theme folder or a folder within WP_CONTENT_DIR
  362. * @return bool
  363. */
  364. function register_theme_directory( $directory ) {
  365. global $wp_theme_directories;
  366. if ( ! file_exists( $directory ) ) {
  367. // Try prepending as the theme directory could be relative to the content directory.
  368. $directory = WP_CONTENT_DIR . '/' . $directory;
  369. // If this directory does not exist, return and do not register.
  370. if ( ! file_exists( $directory ) ) {
  371. return false;
  372. }
  373. }
  374. if ( ! is_array( $wp_theme_directories ) ) {
  375. $wp_theme_directories = array();
  376. }
  377. $untrailed = untrailingslashit( $directory );
  378. if ( ! empty( $untrailed ) && ! in_array( $untrailed, $wp_theme_directories ) ) {
  379. $wp_theme_directories[] = $untrailed;
  380. }
  381. return true;
  382. }
  383. /**
  384. * Search all registered theme directories for complete and valid themes.
  385. *
  386. * @since 2.9.0
  387. *
  388. * @global array $wp_theme_directories
  389. * @staticvar array $found_themes
  390. *
  391. * @param bool $force Optional. Whether to force a new directory scan. Defaults to false.
  392. * @return array|false Valid themes found
  393. */
  394. function search_theme_directories( $force = false ) {
  395. global $wp_theme_directories;
  396. static $found_themes = null;
  397. if ( empty( $wp_theme_directories ) ) {
  398. return false;
  399. }
  400. if ( ! $force && isset( $found_themes ) ) {
  401. return $found_themes;
  402. }
  403. $found_themes = array();
  404. $wp_theme_directories = (array) $wp_theme_directories;
  405. $relative_theme_roots = array();
  406. /*
  407. * Set up maybe-relative, maybe-absolute array of theme directories.
  408. * We always want to return absolute, but we need to cache relative
  409. * to use in get_theme_root().
  410. */
  411. foreach ( $wp_theme_directories as $theme_root ) {
  412. if ( 0 === strpos( $theme_root, WP_CONTENT_DIR ) ) {
  413. $relative_theme_roots[ str_replace( WP_CONTENT_DIR, '', $theme_root ) ] = $theme_root;
  414. } else {
  415. $relative_theme_roots[ $theme_root ] = $theme_root;
  416. }
  417. }
  418. /**
  419. * Filters whether to get the cache of the registered theme directories.
  420. *
  421. * @since 3.4.0
  422. *
  423. * @param bool $cache_expiration Whether to get the cache of the theme directories. Default false.
  424. * @param string $cache_directory Directory to be searched for the cache.
  425. */
  426. $cache_expiration = apply_filters( 'wp_cache_themes_persistently', false, 'search_theme_directories' );
  427. if ( $cache_expiration ) {
  428. $cached_roots = get_site_transient( 'theme_roots' );
  429. if ( is_array( $cached_roots ) ) {
  430. foreach ( $cached_roots as $theme_dir => $theme_root ) {
  431. // A cached theme root is no longer around, so skip it.
  432. if ( ! isset( $relative_theme_roots[ $theme_root ] ) ) {
  433. continue;
  434. }
  435. $found_themes[ $theme_dir ] = array(
  436. 'theme_file' => $theme_dir . '/style.css',
  437. 'theme_root' => $relative_theme_roots[ $theme_root ], // Convert relative to absolute.
  438. );
  439. }
  440. return $found_themes;
  441. }
  442. if ( ! is_int( $cache_expiration ) ) {
  443. $cache_expiration = 30 * MINUTE_IN_SECONDS;
  444. }
  445. } else {
  446. $cache_expiration = 30 * MINUTE_IN_SECONDS;
  447. }
  448. /* Loop the registered theme directories and extract all themes */
  449. foreach ( $wp_theme_directories as $theme_root ) {
  450. // Start with directories in the root of the current theme directory.
  451. $dirs = @ scandir( $theme_root );
  452. if ( ! $dirs ) {
  453. trigger_error( "$theme_root is not readable", E_USER_NOTICE );
  454. continue;
  455. }
  456. foreach ( $dirs as $dir ) {
  457. if ( ! is_dir( $theme_root . '/' . $dir ) || '.' === $dir[0] || 'CVS' === $dir ) {
  458. continue;
  459. }
  460. if ( file_exists( $theme_root . '/' . $dir . '/style.css' ) ) {
  461. // wp-content/themes/a-single-theme
  462. // wp-content/themes is $theme_root, a-single-theme is $dir.
  463. $found_themes[ $dir ] = array(
  464. 'theme_file' => $dir . '/style.css',
  465. 'theme_root' => $theme_root,
  466. );
  467. } else {
  468. $found_theme = false;
  469. // wp-content/themes/a-folder-of-themes/*
  470. // wp-content/themes is $theme_root, a-folder-of-themes is $dir, then themes are $sub_dirs.
  471. $sub_dirs = @ scandir( $theme_root . '/' . $dir );
  472. if ( ! $sub_dirs ) {
  473. trigger_error( "$theme_root/$dir is not readable", E_USER_NOTICE );
  474. continue;
  475. }
  476. foreach ( $sub_dirs as $sub_dir ) {
  477. if ( ! is_dir( $theme_root . '/' . $dir . '/' . $sub_dir ) || '.' === $dir[0] || 'CVS' === $dir ) {
  478. continue;
  479. }
  480. if ( ! file_exists( $theme_root . '/' . $dir . '/' . $sub_dir . '/style.css' ) ) {
  481. continue;
  482. }
  483. $found_themes[ $dir . '/' . $sub_dir ] = array(
  484. 'theme_file' => $dir . '/' . $sub_dir . '/style.css',
  485. 'theme_root' => $theme_root,
  486. );
  487. $found_theme = true;
  488. }
  489. // Never mind the above, it's just a theme missing a style.css.
  490. // Return it; WP_Theme will catch the error.
  491. if ( ! $found_theme ) {
  492. $found_themes[ $dir ] = array(
  493. 'theme_file' => $dir . '/style.css',
  494. 'theme_root' => $theme_root,
  495. );
  496. }
  497. }
  498. }
  499. }
  500. asort( $found_themes );
  501. $theme_roots = array();
  502. $relative_theme_roots = array_flip( $relative_theme_roots );
  503. foreach ( $found_themes as $theme_dir => $theme_data ) {
  504. $theme_roots[ $theme_dir ] = $relative_theme_roots[ $theme_data['theme_root'] ]; // Convert absolute to relative.
  505. }
  506. if ( get_site_transient( 'theme_roots' ) != $theme_roots ) {
  507. set_site_transient( 'theme_roots', $theme_roots, $cache_expiration );
  508. }
  509. return $found_themes;
  510. }
  511. /**
  512. * Retrieve path to themes directory.
  513. *
  514. * Does not have trailing slash.
  515. *
  516. * @since 1.5.0
  517. *
  518. * @global array $wp_theme_directories
  519. *
  520. * @param string $stylesheet_or_template Optional. The stylesheet or template name of the theme.
  521. * Default is to leverage the main theme root.
  522. * @return string Themes directory path.
  523. */
  524. function get_theme_root( $stylesheet_or_template = '' ) {
  525. global $wp_theme_directories;
  526. $theme_root = '';
  527. if ( $stylesheet_or_template ) {
  528. $theme_root = get_raw_theme_root( $stylesheet_or_template );
  529. if ( $theme_root ) {
  530. // Always prepend WP_CONTENT_DIR unless the root currently registered as a theme directory.
  531. // This gives relative theme roots the benefit of the doubt when things go haywire.
  532. if ( ! in_array( $theme_root, (array) $wp_theme_directories ) ) {
  533. $theme_root = WP_CONTENT_DIR . $theme_root;
  534. }
  535. }
  536. }
  537. if ( ! $theme_root ) {
  538. $theme_root = WP_CONTENT_DIR . '/themes';
  539. }
  540. /**
  541. * Filters the absolute path to the themes directory.
  542. *
  543. * @since 1.5.0
  544. *
  545. * @param string $theme_root Absolute path to themes directory.
  546. */
  547. return apply_filters( 'theme_root', $theme_root );
  548. }
  549. /**
  550. * Retrieve URI for themes directory.
  551. *
  552. * Does not have trailing slash.
  553. *
  554. * @since 1.5.0
  555. *
  556. * @global array $wp_theme_directories
  557. *
  558. * @param string $stylesheet_or_template Optional. The stylesheet or template name of the theme.
  559. * Default is to leverage the main theme root.
  560. * @param string $theme_root Optional. The theme root for which calculations will be based,
  561. * preventing the need for a get_raw_theme_root() call. Default empty.
  562. * @return string Themes directory URI.
  563. */
  564. function get_theme_root_uri( $stylesheet_or_template = '', $theme_root = '' ) {
  565. global $wp_theme_directories;
  566. if ( $stylesheet_or_template && ! $theme_root ) {
  567. $theme_root = get_raw_theme_root( $stylesheet_or_template );
  568. }
  569. if ( $stylesheet_or_template && $theme_root ) {
  570. if ( in_array( $theme_root, (array) $wp_theme_directories ) ) {
  571. // Absolute path. Make an educated guess. YMMV -- but note the filter below.
  572. if ( 0 === strpos( $theme_root, WP_CONTENT_DIR ) ) {
  573. $theme_root_uri = content_url( str_replace( WP_CONTENT_DIR, '', $theme_root ) );
  574. } elseif ( 0 === strpos( $theme_root, ABSPATH ) ) {
  575. $theme_root_uri = site_url( str_replace( ABSPATH, '', $theme_root ) );
  576. } elseif ( 0 === strpos( $theme_root, WP_PLUGIN_DIR ) || 0 === strpos( $theme_root, WPMU_PLUGIN_DIR ) ) {
  577. $theme_root_uri = plugins_url( basename( $theme_root ), $theme_root );
  578. } else {
  579. $theme_root_uri = $theme_root;
  580. }
  581. } else {
  582. $theme_root_uri = content_url( $theme_root );
  583. }
  584. } else {
  585. $theme_root_uri = content_url( 'themes' );
  586. }
  587. /**
  588. * Filters the URI for themes directory.
  589. *
  590. * @since 1.5.0
  591. *
  592. * @param string $theme_root_uri The URI for themes directory.
  593. * @param string $siteurl WordPress web address which is set in General Options.
  594. * @param string $stylesheet_or_template The stylesheet or template name of the theme.
  595. */
  596. return apply_filters( 'theme_root_uri', $theme_root_uri, get_option( 'siteurl' ), $stylesheet_or_template );
  597. }
  598. /**
  599. * Get the raw theme root relative to the content directory with no filters applied.
  600. *
  601. * @since 3.1.0
  602. *
  603. * @global array $wp_theme_directories
  604. *
  605. * @param string $stylesheet_or_template The stylesheet or template name of the theme.
  606. * @param bool $skip_cache Optional. Whether to skip the cache.
  607. * Defaults to false, meaning the cache is used.
  608. * @return string Theme root.
  609. */
  610. function get_raw_theme_root( $stylesheet_or_template, $skip_cache = false ) {
  611. global $wp_theme_directories;
  612. if ( ! is_array( $wp_theme_directories ) || count( $wp_theme_directories ) <= 1 ) {
  613. return '/themes';
  614. }
  615. $theme_root = false;
  616. // If requesting the root for the current theme, consult options to avoid calling get_theme_roots().
  617. if ( ! $skip_cache ) {
  618. if ( get_option( 'stylesheet' ) == $stylesheet_or_template ) {
  619. $theme_root = get_option( 'stylesheet_root' );
  620. } elseif ( get_option( 'template' ) == $stylesheet_or_template ) {
  621. $theme_root = get_option( 'template_root' );
  622. }
  623. }
  624. if ( empty( $theme_root ) ) {
  625. $theme_roots = get_theme_roots();
  626. if ( ! empty( $theme_roots[ $stylesheet_or_template ] ) ) {
  627. $theme_root = $theme_roots[ $stylesheet_or_template ];
  628. }
  629. }
  630. return $theme_root;
  631. }
  632. /**
  633. * Display localized stylesheet link element.
  634. *
  635. * @since 2.1.0
  636. */
  637. function locale_stylesheet() {
  638. $stylesheet = get_locale_stylesheet_uri();
  639. if ( empty( $stylesheet ) ) {
  640. return;
  641. }
  642. $type_attr = current_theme_supports( 'html5', 'style' ) ? '' : ' type="text/css"';
  643. printf(
  644. '<link rel="stylesheet" href="%s"%s media="screen" />',
  645. $stylesheet,
  646. $type_attr
  647. );
  648. }
  649. /**
  650. * Switches the theme.
  651. *
  652. * Accepts one argument: $stylesheet of the theme. It also accepts an additional function signature
  653. * of two arguments: $template then $stylesheet. This is for backward compatibility.
  654. *
  655. * @since 2.5.0
  656. *
  657. * @global array $wp_theme_directories
  658. * @global WP_Customize_Manager $wp_customize
  659. * @global array $sidebars_widgets
  660. *
  661. * @param string $stylesheet Stylesheet name
  662. */
  663. function switch_theme( $stylesheet ) {
  664. global $wp_theme_directories, $wp_customize, $sidebars_widgets;
  665. $_sidebars_widgets = null;
  666. if ( 'wp_ajax_customize_save' === current_action() ) {
  667. $old_sidebars_widgets_data_setting = $wp_customize->get_setting( 'old_sidebars_widgets_data' );
  668. if ( $old_sidebars_widgets_data_setting ) {
  669. $_sidebars_widgets = $wp_customize->post_value( $old_sidebars_widgets_data_setting );
  670. }
  671. } elseif ( is_array( $sidebars_widgets ) ) {
  672. $_sidebars_widgets = $sidebars_widgets;
  673. }
  674. if ( is_array( $_sidebars_widgets ) ) {
  675. set_theme_mod(
  676. 'sidebars_widgets',
  677. array(
  678. 'time' => time(),
  679. 'data' => $_sidebars_widgets,
  680. )
  681. );
  682. }
  683. $nav_menu_locations = get_theme_mod( 'nav_menu_locations' );
  684. update_option( 'theme_switch_menu_locations', $nav_menu_locations );
  685. if ( func_num_args() > 1 ) {
  686. $stylesheet = func_get_arg( 1 );
  687. }
  688. $old_theme = wp_get_theme();
  689. $new_theme = wp_get_theme( $stylesheet );
  690. $template = $new_theme->get_template();
  691. if ( wp_is_recovery_mode() ) {
  692. $paused_themes = wp_paused_themes();
  693. $paused_themes->delete( $old_theme->get_stylesheet() );
  694. $paused_themes->delete( $old_theme->get_template() );
  695. }
  696. update_option( 'template', $template );
  697. update_option( 'stylesheet', $stylesheet );
  698. if ( count( $wp_theme_directories ) > 1 ) {
  699. update_option( 'template_root', get_raw_theme_root( $template, true ) );
  700. update_option( 'stylesheet_root', get_raw_theme_root( $stylesheet, true ) );
  701. } else {
  702. delete_option( 'template_root' );
  703. delete_option( 'stylesheet_root' );
  704. }
  705. $new_name = $new_theme->get( 'Name' );
  706. update_option( 'current_theme', $new_name );
  707. // Migrate from the old mods_{name} option to theme_mods_{slug}.
  708. if ( is_admin() && false === get_option( 'theme_mods_' . $stylesheet ) ) {
  709. $default_theme_mods = (array) get_option( 'mods_' . $new_name );
  710. if ( ! empty( $nav_menu_locations ) && empty( $default_theme_mods['nav_menu_locations'] ) ) {
  711. $default_theme_mods['nav_menu_locations'] = $nav_menu_locations;
  712. }
  713. add_option( "theme_mods_$stylesheet", $default_theme_mods );
  714. } else {
  715. /*
  716. * Since retrieve_widgets() is called when initializing a theme in the Customizer,
  717. * we need to remove the theme mods to avoid overwriting changes made via
  718. * the Customizer when accessing wp-admin/widgets.php.
  719. */
  720. if ( 'wp_ajax_customize_save' === current_action() ) {
  721. remove_theme_mod( 'sidebars_widgets' );
  722. }
  723. }
  724. update_option( 'theme_switched', $old_theme->get_stylesheet() );
  725. /**
  726. * Fires after the theme is switched.
  727. *
  728. * @since 1.5.0
  729. * @since 4.5.0 Introduced the `$old_theme` parameter.
  730. *
  731. * @param string $new_name Name of the new theme.
  732. * @param WP_Theme $new_theme WP_Theme instance of the new theme.
  733. * @param WP_Theme $old_theme WP_Theme instance of the old theme.
  734. */
  735. do_action( 'switch_theme', $new_name, $new_theme, $old_theme );
  736. }
  737. /**
  738. * Checks that current theme files 'index.php' and 'style.css' exists.
  739. *
  740. * Does not initially check the default theme, which is the fallback and should always exist.
  741. * But if it doesn't exist, it'll fall back to the latest core default theme that does exist.
  742. * Will switch theme to the fallback theme if current theme does not validate.
  743. *
  744. * You can use the {@see 'validate_current_theme'} filter to return false to
  745. * disable this functionality.
  746. *
  747. * @since 1.5.0
  748. * @see WP_DEFAULT_THEME
  749. *
  750. * @return bool
  751. */
  752. function validate_current_theme() {
  753. /**
  754. * Filters whether to validate the current theme.
  755. *
  756. * @since 2.7.0
  757. *
  758. * @param bool $validate Whether to validate the current theme. Default true.
  759. */
  760. if ( wp_installing() || ! apply_filters( 'validate_current_theme', true ) ) {
  761. return true;
  762. }
  763. if ( ! file_exists( get_template_directory() . '/index.php' ) ) {
  764. // Invalid.
  765. } elseif ( ! file_exists( get_template_directory() . '/style.css' ) ) {
  766. // Invalid.
  767. } elseif ( is_child_theme() && ! file_exists( get_stylesheet_directory() . '/style.css' ) ) {
  768. // Invalid.
  769. } else {
  770. // Valid.
  771. return true;
  772. }
  773. $default = wp_get_theme( WP_DEFAULT_THEME );
  774. if ( $default->exists() ) {
  775. switch_theme( WP_DEFAULT_THEME );
  776. return false;
  777. }
  778. /**
  779. * If we're in an invalid state but WP_DEFAULT_THEME doesn't exist,
  780. * switch to the latest core default theme that's installed.
  781. * If it turns out that this latest core default theme is our current
  782. * theme, then there's nothing we can do about that, so we have to bail,
  783. * rather than going into an infinite loop. (This is why there are
  784. * checks against WP_DEFAULT_THEME above, also.) We also can't do anything
  785. * if it turns out there is no default theme installed. (That's `false`.)
  786. */
  787. $default = WP_Theme::get_core_default_theme();
  788. if ( false === $default || get_stylesheet() == $default->get_stylesheet() ) {
  789. return true;
  790. }
  791. switch_theme( $default->get_stylesheet() );
  792. return false;
  793. }
  794. /**
  795. * Retrieve all theme modifications.
  796. *
  797. * @since 3.1.0
  798. *
  799. * @return array|void Theme modifications.
  800. */
  801. function get_theme_mods() {
  802. $theme_slug = get_option( 'stylesheet' );
  803. $mods = get_option( "theme_mods_$theme_slug" );
  804. if ( false === $mods ) {
  805. $theme_name = get_option( 'current_theme' );
  806. if ( false === $theme_name ) {
  807. $theme_name = wp_get_theme()->get( 'Name' );
  808. }
  809. $mods = get_option( "mods_$theme_name" ); // Deprecated location.
  810. if ( is_admin() && false !== $mods ) {
  811. update_option( "theme_mods_$theme_slug", $mods );
  812. delete_option( "mods_$theme_name" );
  813. }
  814. }
  815. return $mods;
  816. }
  817. /**
  818. * Retrieve theme modification value for the current theme.
  819. *
  820. * If the modification name does not exist, then the $default will be passed
  821. * through {@link https://www.php.net/sprintf sprintf()} PHP function with
  822. * the template directory URI as the first string and the stylesheet directory URI
  823. * as the second string.
  824. *
  825. * @since 2.1.0
  826. *
  827. * @param string $name Theme modification name.
  828. * @param string|false $default Optional. Theme modification default value. Default false.
  829. * @return mixed Theme modification value.
  830. */
  831. function get_theme_mod( $name, $default = false ) {
  832. $mods = get_theme_mods();
  833. if ( isset( $mods[ $name ] ) ) {
  834. /**
  835. * Filters the theme modification, or 'theme_mod', value.
  836. *
  837. * The dynamic portion of the hook name, `$name`, refers to the key name
  838. * of the modification array. For example, 'header_textcolor', 'header_image',
  839. * and so on depending on the theme options.
  840. *
  841. * @since 2.2.0
  842. *
  843. * @param string $current_mod The value of the current theme modification.
  844. */
  845. return apply_filters( "theme_mod_{$name}", $mods[ $name ] );
  846. }
  847. if ( is_string( $default ) ) {
  848. // Only run the replacement if an sprintf() string format pattern was found.
  849. if ( preg_match( '#(?<!%)%(?:\d+\$?)?s#', $default ) ) {
  850. $default = sprintf( $default, get_template_directory_uri(), get_stylesheet_directory_uri() );
  851. }
  852. }
  853. /** This filter is documented in wp-includes/theme.php */
  854. return apply_filters( "theme_mod_{$name}", $default );
  855. }
  856. /**
  857. * Update theme modification value for the current theme.
  858. *
  859. * @since 2.1.0
  860. *
  861. * @param string $name Theme modification name.
  862. * @param mixed $value Theme modification value.
  863. */
  864. function set_theme_mod( $name, $value ) {
  865. $mods = get_theme_mods();
  866. $old_value = isset( $mods[ $name ] ) ? $mods[ $name ] : false;
  867. /**
  868. * Filters the theme modification, or 'theme_mod', value on save.
  869. *
  870. * The dynamic portion of the hook name, `$name`, refers to the key name
  871. * of the modification array. For example, 'header_textcolor', 'header_image',
  872. * and so on depending on the theme options.
  873. *
  874. * @since 3.9.0
  875. *
  876. * @param string $value The new value of the theme modification.
  877. * @param string $old_value The current value of the theme modification.
  878. */
  879. $mods[ $name ] = apply_filters( "pre_set_theme_mod_{$name}", $value, $old_value );
  880. $theme = get_option( 'stylesheet' );
  881. update_option( "theme_mods_$theme", $mods );
  882. }
  883. /**
  884. * Remove theme modification name from current theme list.
  885. *
  886. * If removing the name also removes all elements, then the entire option will
  887. * be removed.
  888. *
  889. * @since 2.1.0
  890. *
  891. * @param string $name Theme modification name.
  892. */
  893. function remove_theme_mod( $name ) {
  894. $mods = get_theme_mods();
  895. if ( ! isset( $mods[ $name ] ) ) {
  896. return;
  897. }
  898. unset( $mods[ $name ] );
  899. if ( empty( $mods ) ) {
  900. remove_theme_mods();
  901. return;
  902. }
  903. $theme = get_option( 'stylesheet' );
  904. update_option( "theme_mods_$theme", $mods );
  905. }
  906. /**
  907. * Remove theme modifications option for current theme.
  908. *
  909. * @since 2.1.0
  910. */
  911. function remove_theme_mods() {
  912. delete_option( 'theme_mods_' . get_option( 'stylesheet' ) );
  913. // Old style.
  914. $theme_name = get_option( 'current_theme' );
  915. if ( false === $theme_name ) {
  916. $theme_name = wp_get_theme()->get( 'Name' );
  917. }
  918. delete_option( 'mods_' . $theme_name );
  919. }
  920. /**
  921. * Retrieves the custom header text color in 3- or 6-digit hexadecimal form.
  922. *
  923. * @since 2.1.0
  924. *
  925. * @return string Header text color in 3- or 6-digit hexadecimal form (minus the hash symbol).
  926. */
  927. function get_header_textcolor() {
  928. return get_theme_mod( 'header_textcolor', get_theme_support( 'custom-header', 'default-text-color' ) );
  929. }
  930. /**
  931. * Displays the custom header text color in 3- or 6-digit hexadecimal form (minus the hash symbol).
  932. *
  933. * @since 2.1.0
  934. */
  935. function header_textcolor() {
  936. echo get_header_textcolor();
  937. }
  938. /**
  939. * Whether to display the header text.
  940. *
  941. * @since 3.4.0
  942. *
  943. * @return bool
  944. */
  945. function display_header_text() {
  946. if ( ! current_theme_supports( 'custom-header', 'header-text' ) ) {
  947. return false;
  948. }
  949. $text_color = get_theme_mod( 'header_textcolor', get_theme_support( 'custom-header', 'default-text-color' ) );
  950. return 'blank' !== $text_color;
  951. }
  952. /**
  953. * Check whether a header image is set or not.
  954. *
  955. * @since 4.2.0
  956. *
  957. * @see get_header_image()
  958. *
  959. * @return bool Whether a header image is set or not.
  960. */
  961. function has_header_image() {
  962. return (bool) get_header_image();
  963. }
  964. /**
  965. * Retrieve header image for custom header.
  966. *
  967. * @since 2.1.0
  968. *
  969. * @return string|false
  970. */
  971. function get_header_image() {
  972. $url = get_theme_mod( 'header_image', get_theme_support( 'custom-header', 'default-image' ) );
  973. if ( 'remove-header' == $url ) {
  974. return false;
  975. }
  976. if ( is_random_header_image() ) {
  977. $url = get_random_header_image();
  978. }
  979. return esc_url_raw( set_url_scheme( $url ) );
  980. }
  981. /**
  982. * Create image tag markup for a custom header image.
  983. *
  984. * @since 4.4.0
  985. *
  986. * @param array $attr Optional. Additional attributes for the image tag. Can be used
  987. * to override the default attributes. Default empty.
  988. * @return string HTML image element markup or empty string on failure.
  989. */
  990. function get_header_image_tag( $attr = array() ) {
  991. $header = get_custom_header();
  992. $header->url = get_header_image();
  993. if ( ! $header->url ) {
  994. return '';
  995. }
  996. $width = absint( $header->width );
  997. $height = absint( $header->height );
  998. $attr = wp_parse_args(
  999. $attr,
  1000. array(
  1001. 'src' => $header->url,
  1002. 'width' => $width,
  1003. 'height' => $height,
  1004. 'alt' => get_bloginfo( 'name' ),
  1005. )
  1006. );
  1007. // Generate 'srcset' and 'sizes' if not already present.
  1008. if ( empty( $attr['srcset'] ) && ! empty( $header->attachment_id ) ) {
  1009. $image_meta = get_post_meta( $header->attachment_id, '_wp_attachment_metadata', true );
  1010. $size_array = array( $width, $height );
  1011. if ( is_array( $image_meta ) ) {
  1012. $srcset = wp_calculate_image_srcset( $size_array, $header->url, $image_meta, $header->attachment_id );
  1013. $sizes = ! empty( $attr['sizes'] ) ? $attr['sizes'] : wp_calculate_image_sizes( $size_array, $header->url, $image_meta, $header->attachment_id );
  1014. if ( $srcset && $sizes ) {
  1015. $attr['srcset'] = $srcset;
  1016. $attr['sizes'] = $sizes;
  1017. }
  1018. }
  1019. }
  1020. $attr = array_map( 'esc_attr', $attr );
  1021. $html = '<img';
  1022. foreach ( $attr as $name => $value ) {
  1023. $html .= ' ' . $name . '="' . $value . '"';
  1024. }
  1025. $html .= ' />';
  1026. /**
  1027. * Filters the markup of header images.
  1028. *
  1029. * @since 4.4.0
  1030. *
  1031. * @param string $html The HTML image tag markup being filtered.
  1032. * @param object $header The custom header object returned by 'get_custom_header()'.
  1033. * @param array $attr Array of the attributes for the image tag.
  1034. */
  1035. return apply_filters( 'get_header_image_tag', $html, $header, $attr );
  1036. }
  1037. /**
  1038. * Display the image markup for a custom header image.
  1039. *
  1040. * @since 4.4.0
  1041. *
  1042. * @param array $attr Optional. Attributes for the image markup. Default empty.
  1043. */
  1044. function the_header_image_tag( $attr = array() ) {
  1045. echo get_header_image_tag( $attr );
  1046. }
  1047. /**
  1048. * Get random header image data from registered images in theme.
  1049. *
  1050. * @since 3.4.0
  1051. *
  1052. * @access private
  1053. *
  1054. * @global array $_wp_default_headers
  1055. * @staticvar object $_wp_random_header
  1056. *
  1057. * @return object
  1058. */
  1059. function _get_random_header_data() {
  1060. static $_wp_random_header = null;
  1061. if ( empty( $_wp_random_header ) ) {
  1062. global $_wp_default_headers;
  1063. $header_image_mod = get_theme_mod( 'header_image', '' );
  1064. $headers = array();
  1065. if ( 'random-uploaded-image' == $header_image_mod ) {
  1066. $headers = get_uploaded_header_images();
  1067. } elseif ( ! empty( $_wp_default_headers ) ) {
  1068. if ( 'random-default-image' == $header_image_mod ) {
  1069. $headers = $_wp_default_headers;
  1070. } else {
  1071. if ( current_theme_supports( 'custom-header', 'random-default' ) ) {
  1072. $headers = $_wp_default_headers;
  1073. }
  1074. }
  1075. }
  1076. if ( empty( $headers ) ) {
  1077. return new stdClass;
  1078. }
  1079. $_wp_random_header = (object) $headers[ array_rand( $headers ) ];
  1080. $_wp_random_header->url = sprintf( $_wp_random_header->url, get_template_directory_uri(), get_stylesheet_directory_uri() );
  1081. $_wp_random_header->thumbnail_url = sprintf( $_wp_random_header->thumbnail_url, get_template_directory_uri(), get_stylesheet_directory_uri() );
  1082. }
  1083. return $_wp_random_header;
  1084. }
  1085. /**
  1086. * Get random header image url from registered images in theme.
  1087. *
  1088. * @since 3.2.0
  1089. *
  1090. * @return string Path to header image
  1091. */
  1092. function get_random_header_image() {
  1093. $random_image = _get_random_header_data();
  1094. if ( empty( $random_image->url ) ) {
  1095. return '';
  1096. }
  1097. return $random_image->url;
  1098. }
  1099. /**
  1100. * Check if random header image is in use.
  1101. *
  1102. * Always true if user expressly chooses the option in Appearance > Header.
  1103. * Also true if theme has multiple header images registered, no specific header image
  1104. * is chosen, and theme turns on random headers with add_theme_support().
  1105. *
  1106. * @since 3.2.0
  1107. *
  1108. * @param string $type The random pool to use. any|default|uploaded
  1109. * @return bool
  1110. */
  1111. function is_random_header_image( $type = 'any' ) {
  1112. $header_image_mod = get_theme_mod( 'header_image', get_theme_support( 'custom-header', 'default-image' ) );
  1113. if ( 'any' == $type ) {
  1114. if ( 'random-default-image' == $header_image_mod || 'random-uploaded-image' == $header_image_mod || ( '' != get_random_header_image() && empty( $header_image_mod ) ) ) {
  1115. return true;
  1116. }
  1117. } else {
  1118. if ( "random-$type-image" == $header_image_mod ) {
  1119. return true;
  1120. } elseif ( 'default' == $type && empty( $header_image_mod ) && '' != get_random_header_image() ) {
  1121. return true;
  1122. }
  1123. }
  1124. return false;
  1125. }
  1126. /**
  1127. * Display header image URL.
  1128. *
  1129. * @since 2.1.0
  1130. */
  1131. function header_image() {
  1132. $image = get_header_image();
  1133. if ( $image ) {
  1134. echo esc_url( $image );
  1135. }
  1136. }
  1137. /**
  1138. * Get the header images uploaded for the current theme.
  1139. *
  1140. * @since 3.2.0
  1141. *
  1142. * @return array
  1143. */
  1144. function get_uploaded_header_images() {
  1145. $header_images = array();
  1146. // @todo Caching.
  1147. $headers = get_posts(
  1148. array(
  1149. 'post_type' => 'attachment',
  1150. 'meta_key' => '_wp_attachment_is_custom_header',
  1151. 'meta_value' => get_option( 'stylesheet' ),
  1152. 'orderby' => 'none',
  1153. 'nopaging' => true,
  1154. )
  1155. );
  1156. if ( empty( $headers ) ) {
  1157. return array();
  1158. }
  1159. foreach ( (array) $headers as $header ) {
  1160. $url = esc_url_raw( wp_get_attachment_url( $header->ID ) );
  1161. $header_data = wp_get_attachment_metadata( $header->ID );
  1162. $header_index = $header->ID;
  1163. $header_images[ $header_index ] = array();
  1164. $header_images[ $header_index ]['attachment_id'] = $header->ID;
  1165. $header_images[ $header_index ]['url'] = $url;
  1166. $header_images[ $header_index ]['thumbnail_url'] = $url;
  1167. $header_images[ $header_index ]['alt_text'] = get_post_meta( $header->ID, '_wp_attachment_image_alt', true );
  1168. $header_images[ $header_index ]['attachment_parent'] = isset( $header_data['attachment_parent'] ) ? $header_data['attachment_parent'] : '';
  1169. if ( isset( $header_data['width'] ) ) {
  1170. $header_images[ $header_index ]['width'] = $header_data['width'];
  1171. }
  1172. if ( isset( $header_data['height'] ) ) {
  1173. $header_images[ $header_index ]['height'] = $header_data['height'];
  1174. }
  1175. }
  1176. return $header_images;
  1177. }
  1178. /**
  1179. * Get the header image data.
  1180. *
  1181. * @since 3.4.0
  1182. *
  1183. * @global array $_wp_default_headers
  1184. *
  1185. * @return object
  1186. */
  1187. function get_custom_header() {
  1188. global $_wp_default_headers;
  1189. if ( is_random_header_image() ) {
  1190. $data = _get_random_header_data();
  1191. } else {
  1192. $data = get_theme_mod( 'header_image_data' );
  1193. if ( ! $data && current_theme_supports( 'custom-header', 'default-image' ) ) {
  1194. $directory_args = array( get_template_directory_uri(), get_stylesheet_directory_uri() );
  1195. $data = array();
  1196. $data['url'] = vsprintf( get_theme_support( 'custom-header', 'default-image' ), $directory_args );
  1197. $data['thumbnail_url'] = $data['url'];
  1198. if ( ! empty( $_wp_default_headers ) ) {
  1199. foreach ( (array) $_wp_default_headers as $default_header ) {
  1200. $url = vsprintf( $default_header['url'], $directory_args );
  1201. if ( $data['url'] == $url ) {
  1202. $data = $default_header;
  1203. $data['url'] = $url;
  1204. $data['thumbnail_url'] = vsprintf( $data['thumbnail_url'], $directory_args );
  1205. break;
  1206. }
  1207. }
  1208. }
  1209. }
  1210. }
  1211. $default = array(
  1212. 'url' => '',
  1213. 'thumbnail_url' => '',
  1214. 'width' => get_theme_support( 'custom-header', 'width' ),
  1215. 'height' => get_theme_support( 'custom-header', 'height' ),
  1216. 'video' => get_theme_support( 'custom-header', 'video' ),
  1217. );
  1218. return (object) wp_parse_args( $data, $default );
  1219. }
  1220. /**
  1221. * Register a selection of default headers to be displayed by the custom header admin UI.
  1222. *
  1223. * @since 3.0.0
  1224. *
  1225. * @global array $_wp_default_headers
  1226. *
  1227. * @param array $headers Array of headers keyed by a string id. The ids point to arrays containing 'url', 'thumbnail_url', and 'description' keys.
  1228. */
  1229. function register_default_headers( $headers ) {
  1230. global $_wp_default_headers;
  1231. $_wp_default_headers = array_merge( (array) $_wp_default_headers, (array) $headers );
  1232. }
  1233. /**
  1234. * Unregister default headers.
  1235. *
  1236. * This function must be called after register_default_headers() has already added the
  1237. * header you want to remove.
  1238. *
  1239. * @see register_default_headers()
  1240. * @since 3.0.0
  1241. *
  1242. * @global array $_wp_default_headers
  1243. *
  1244. * @param string|array $header The header string id (key of array) to remove, or an array thereof.
  1245. * @return bool|void A single header returns true on success, false on failure.
  1246. * There is currently no return value for multiple headers.
  1247. */
  1248. function unregister_default_headers( $header ) {
  1249. global $_wp_default_headers;
  1250. if ( is_array( $header ) ) {
  1251. array_map( 'unregister_default_headers', $header );
  1252. } elseif ( isset( $_wp_default_headers[ $header ] ) ) {
  1253. unset( $_wp_default_headers[ $header ] );
  1254. return true;
  1255. } else {
  1256. return false;
  1257. }
  1258. }
  1259. /**
  1260. * Check whether a header video is set or not.
  1261. *
  1262. * @since 4.7.0
  1263. *
  1264. * @see get_header_video_url()
  1265. *
  1266. * @return bool Whether a header video is set or not.
  1267. */
  1268. function has_header_video() {
  1269. return (bool) get_header_video_url();
  1270. }
  1271. /**
  1272. * Retrieve header video URL for custom header.
  1273. *
  1274. * Uses a local video if present, or falls back to an external video.
  1275. *
  1276. * @since 4.7.0
  1277. *
  1278. * @return string|false Header video URL or false if there is no video.
  1279. */
  1280. function get_header_video_url() {
  1281. $id = absint( get_theme_mod( 'header_video' ) );
  1282. if ( $id ) {
  1283. // Get the file URL from the attachment ID.
  1284. $url = wp_get_attachment_url( $id );
  1285. } else {
  1286. $url = get_theme_mod( 'external_header_video' );
  1287. }
  1288. /**
  1289. * Filters the header video URL.
  1290. *
  1291. * @since 4.7.3
  1292. *
  1293. * @param string $url Header video URL, if available.
  1294. */
  1295. $url = apply_filters( 'get_header_video_url', $url );
  1296. if ( ! $id && ! $url ) {
  1297. return false;
  1298. }
  1299. return esc_url_raw( set_url_scheme( $url ) );
  1300. }
  1301. /**
  1302. * Display header video URL.
  1303. *
  1304. * @since 4.7.0
  1305. */
  1306. function the_header_video_url() {
  1307. $video = get_header_video_url();
  1308. if ( $video ) {
  1309. echo esc_url( $video );
  1310. }
  1311. }
  1312. /**
  1313. * Retrieve header video settings.
  1314. *
  1315. * @since 4.7.0
  1316. *
  1317. * @return array
  1318. */
  1319. function get_header_video_settings() {
  1320. $header = get_custom_header();
  1321. $video_url = get_header_video_url();
  1322. $video_type = wp_check_filetype( $video_url, wp_get_mime_types() );
  1323. $settings = array(
  1324. 'mimeType' => '',
  1325. 'posterUrl' => get_header_image(),
  1326. 'videoUrl' => $video_url,
  1327. 'width' => absint( $header->width ),
  1328. 'height' => absint( $header->height ),
  1329. 'minWidth' => 900,
  1330. 'minHeight' => 500,
  1331. 'l10n' => array(
  1332. 'pause' => __( 'Pause' ),
  1333. 'play' => __( 'Play' ),
  1334. 'pauseSpeak' => __( 'Video is paused.' ),
  1335. 'playSpeak' => __( 'Video is playing.' ),
  1336. ),
  1337. );
  1338. if ( preg_match( '#^https?://(?:www\.)?(?:youtube\.com/watch|youtu\.be/)#', $video_url ) ) {
  1339. $settings['mimeType'] = 'video/x-youtube';
  1340. } elseif ( ! empty( $video_type['type'] ) ) {
  1341. $settings['mimeType'] = $video_type['type'];
  1342. }
  1343. /**
  1344. * Filters header video settings.
  1345. *
  1346. * @since 4.7.0
  1347. *
  1348. * @param array $settings An array of header video settings.
  1349. */
  1350. return apply_filters( 'header_video_settings', $settings );
  1351. }
  1352. /**
  1353. * Check whether a custom header is set or not.
  1354. *
  1355. * @since 4.7.0
  1356. *
  1357. * @return bool True if a custom header is set. False if not.
  1358. */
  1359. function has_custom_header() {
  1360. if ( has_header_image() || ( has_header_video() && is_header_video_active() ) ) {
  1361. return true;
  1362. }
  1363. return false;
  1364. }
  1365. /**
  1366. * Checks whether the custom header video is eligible to show on the current page.
  1367. *
  1368. * @since 4.7.0
  1369. *
  1370. * @return bool True if the custom header video should be shown. False if not.
  1371. */
  1372. function is_header_video_active() {
  1373. if ( ! get_theme_support( 'custom-header', 'video' ) ) {
  1374. return false;
  1375. }
  1376. $video_active_cb = get_theme_support( 'custom-header', 'video-active-callback' );
  1377. if ( empty( $video_active_cb ) || ! is_callable( $video_active_cb ) ) {
  1378. $show_video = true;
  1379. } else {
  1380. $show_video = call_user_func( $video_active_cb );
  1381. }
  1382. /**
  1383. * Modify whether the custom header video is eligible to show on the current page.
  1384. *
  1385. * @since 4.7.0
  1386. *
  1387. * @param bool $show_video Whether the custom header video should be shown. Returns the value
  1388. * of the theme setting for the `custom-header`'s `video-active-callback`.
  1389. * If no callback is set, the default value is that of `is_front_page()`.
  1390. */
  1391. return apply_filters( 'is_header_video_active', $show_video );
  1392. }
  1393. /**
  1394. * Retrieve the markup for a custom header.
  1395. *
  1396. * The container div will always be returned in the Customizer preview.
  1397. *
  1398. * @since 4.7.0
  1399. *
  1400. * @return string The markup for a custom header on success.
  1401. */
  1402. function get_custom_header_markup() {
  1403. if ( ! has_custom_header() && ! is_customize_preview() ) {
  1404. return '';
  1405. }
  1406. return sprintf(
  1407. '<div id="wp-custom-header" class="wp-custom-header">%s</div>',
  1408. get_header_image_tag()
  1409. );
  1410. }
  1411. /**
  1412. * Print the markup for a custom header.
  1413. *
  1414. * A container div will always be printed in the Customizer preview.
  1415. *
  1416. * @since 4.7.0
  1417. */
  1418. function the_custom_header_markup() {
  1419. $custom_header = get_custom_header_markup();
  1420. if ( empty( $custom_header ) ) {
  1421. return;
  1422. }
  1423. echo $custom_header;
  1424. if ( is_header_video_active() && ( has_header_video() || is_customize_preview() ) ) {
  1425. wp_enqueue_script( 'wp-custom-header' );
  1426. wp_localize_script( 'wp-custom-header', '_wpCustomHeaderSettings', get_header_video_settings() );
  1427. }
  1428. }
  1429. /**
  1430. * Retrieve background image for custom background.
  1431. *
  1432. * @since 3.0.0
  1433. *
  1434. * @return string
  1435. */
  1436. function get_background_image() {
  1437. return get_theme_mod( 'background_image', get_theme_support( 'custom-background', 'default-image' ) );
  1438. }
  1439. /**
  1440. * Display background image path.
  1441. *
  1442. * @since 3.0.0
  1443. */
  1444. function background_image() {
  1445. echo get_background_image();
  1446. }
  1447. /**
  1448. * Retrieve value for custom background color.
  1449. *
  1450. * @since 3.0.0
  1451. *
  1452. * @return string
  1453. */
  1454. function get_background_color() {
  1455. return get_theme_mod( 'background_color', get_theme_support( 'custom-background', 'default-color' ) );
  1456. }
  1457. /**
  1458. * Display background color value.
  1459. *
  1460. * @since 3.0.0
  1461. */
  1462. function background_color() {
  1463. echo get_background_color();
  1464. }
  1465. /**
  1466. * Default custom background callback.
  1467. *
  1468. * @since 3.0.0
  1469. */
  1470. function _custom_background_cb() {
  1471. // $background is the saved custom image, or the default image.
  1472. $background = set_url_scheme( get_background_image() );
  1473. // $color is the saved custom color.
  1474. // A default has to be specified in style.css. It will not be printed here.
  1475. $color = get_background_color();
  1476. if ( get_theme_support( 'custom-background', 'default-color' ) === $color ) {
  1477. $color = false;
  1478. }
  1479. $type_attr = current_theme_supports( 'html5', 'style' ) ? '' : ' type="text/css"';
  1480. if ( ! $background && ! $color ) {
  1481. if ( is_customize_preview() ) {
  1482. printf( '<style%s id="custom-background-css"></style>', $type_attr );
  1483. }
  1484. return;
  1485. }
  1486. $style = $color ? "background-color: #$color;" : '';
  1487. if ( $background ) {
  1488. $image = ' background-image: url("' . esc_url_raw( $background ) . '");';
  1489. // Background Position.
  1490. $position_x = get_theme_mod( 'background_position_x', get_theme_support( 'custom-background', 'default-position-x' ) );
  1491. $position_y = get_theme_mod( 'background_position_y', get_theme_support( 'custom-background', 'default-position-y' ) );
  1492. if ( ! in_array( $position_x, array( 'left', 'center', 'right' ), true ) ) {
  1493. $position_x = 'left';
  1494. }
  1495. if ( ! in_array( $position_y, array( 'top', 'center', 'bottom' ), true ) ) {
  1496. $position_y = 'top';
  1497. }
  1498. $position = " background-position: $position_x $position_y;";
  1499. // Background Size.
  1500. $size = get_theme_mod( 'background_size', get_theme_support( 'custom-background', 'default-size' ) );
  1501. if ( ! in_array( $size, array( 'auto', 'contain', 'cover' ), true ) ) {
  1502. $size = 'auto';
  1503. }
  1504. $size = " background-size: $size;";
  1505. // Background Repeat.
  1506. $repeat = get_theme_mod( 'background_repeat', get_theme_support( 'custom-background', 'default-repeat' ) );
  1507. if ( ! in_array( $repeat, array( 'repeat-x', 'repeat-y', 'repeat', 'no-repeat' ), true ) ) {
  1508. $repeat = 'repeat';
  1509. }
  1510. $repeat = " background-repeat: $repeat;";
  1511. // Background Scroll.
  1512. $attachment = get_theme_mod( 'background_attachment', get_theme_support( 'custom-background', 'default-attachment' ) );
  1513. if ( 'fixed' !== $attachment ) {
  1514. $attachment = 'scroll';
  1515. }
  1516. $attachment = " background-attachment: $attachment;";
  1517. $style .= $image . $position . $size . $repeat . $attachment;
  1518. }
  1519. ?>
  1520. <style<?php echo $type_attr; ?> id="custom-background-css">
  1521. body.custom-background { <?php echo trim( $style ); ?> }
  1522. </style