PageRenderTime 59ms CodeModel.GetById 20ms RepoModel.GetById 0ms app.codeStats 1ms

/wp-includes/theme.php

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