PageRenderTime 55ms CodeModel.GetById 14ms RepoModel.GetById 0ms app.codeStats 1ms

/wp-includes/theme.php

https://gitlab.com/rio.munas/gddc
PHP | 1633 lines | 749 code | 188 blank | 696 comment | 189 complexity | f8fef22f859031047659f64f7d50d70e 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 them.
  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 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. *
  670. * @param string $new_name Name of the new theme.
  671. * @param WP_Theme $new_theme WP_Theme instance of the new theme.
  672. */
  673. do_action( 'switch_theme', $new_name, $new_theme );
  674. }
  675. /**
  676. * Checks that current theme files 'index.php' and 'style.css' exists.
  677. *
  678. * Does not initially check the default theme, which is the fallback and should always exist.
  679. * But if it doesn't exist, it'll fall back to the latest core default theme that does exist.
  680. * Will switch theme to the fallback theme if current theme does not validate.
  681. *
  682. * You can use the 'validate_current_theme' filter to return false to
  683. * disable this functionality.
  684. *
  685. * @since 1.5.0
  686. * @see WP_DEFAULT_THEME
  687. *
  688. * @return bool
  689. */
  690. function validate_current_theme() {
  691. /**
  692. * Filter whether to validate the current theme.
  693. *
  694. * @since 2.7.0
  695. *
  696. * @param bool true Validation flag to check the current theme.
  697. */
  698. if ( wp_installing() || ! apply_filters( 'validate_current_theme', true ) )
  699. return true;
  700. if ( ! file_exists( get_template_directory() . '/index.php' ) ) {
  701. // Invalid.
  702. } elseif ( ! file_exists( get_template_directory() . '/style.css' ) ) {
  703. // Invalid.
  704. } elseif ( is_child_theme() && ! file_exists( get_stylesheet_directory() . '/style.css' ) ) {
  705. // Invalid.
  706. } else {
  707. // Valid.
  708. return true;
  709. }
  710. $default = wp_get_theme( WP_DEFAULT_THEME );
  711. if ( $default->exists() ) {
  712. switch_theme( WP_DEFAULT_THEME );
  713. return false;
  714. }
  715. /**
  716. * If we're in an invalid state but WP_DEFAULT_THEME doesn't exist,
  717. * switch to the latest core default theme that's installed.
  718. * If it turns out that this latest core default theme is our current
  719. * theme, then there's nothing we can do about that, so we have to bail,
  720. * rather than going into an infinite loop. (This is why there are
  721. * checks against WP_DEFAULT_THEME above, also.) We also can't do anything
  722. * if it turns out there is no default theme installed. (That's `false`.)
  723. */
  724. $default = WP_Theme::get_core_default_theme();
  725. if ( false === $default || get_stylesheet() == $default->get_stylesheet() ) {
  726. return true;
  727. }
  728. switch_theme( $default->get_stylesheet() );
  729. return false;
  730. }
  731. /**
  732. * Retrieve all theme modifications.
  733. *
  734. * @since 3.1.0
  735. *
  736. * @return array|void Theme modifications.
  737. */
  738. function get_theme_mods() {
  739. $theme_slug = get_option( 'stylesheet' );
  740. $mods = get_option( "theme_mods_$theme_slug" );
  741. if ( false === $mods ) {
  742. $theme_name = get_option( 'current_theme' );
  743. if ( false === $theme_name )
  744. $theme_name = wp_get_theme()->get('Name');
  745. $mods = get_option( "mods_$theme_name" ); // Deprecated location.
  746. if ( is_admin() && false !== $mods ) {
  747. update_option( "theme_mods_$theme_slug", $mods );
  748. delete_option( "mods_$theme_name" );
  749. }
  750. }
  751. return $mods;
  752. }
  753. /**
  754. * Retrieve theme modification value for the current theme.
  755. *
  756. * If the modification name does not exist, then the $default will be passed
  757. * through {@link http://php.net/sprintf sprintf()} PHP function with the first
  758. * string the template directory URI and the second string the stylesheet
  759. * directory URI.
  760. *
  761. * @since 2.1.0
  762. *
  763. * @param string $name Theme modification name.
  764. * @param bool|string $default
  765. * @return string
  766. */
  767. function get_theme_mod( $name, $default = false ) {
  768. $mods = get_theme_mods();
  769. if ( isset( $mods[$name] ) ) {
  770. /**
  771. * Filter the theme modification, or 'theme_mod', value.
  772. *
  773. * The dynamic portion of the hook name, `$name`, refers to
  774. * the key name of the modification array. For example,
  775. * 'header_textcolor', 'header_image', and so on depending
  776. * on the theme options.
  777. *
  778. * @since 2.2.0
  779. *
  780. * @param string $current_mod The value of the current theme modification.
  781. */
  782. return apply_filters( "theme_mod_{$name}", $mods[$name] );
  783. }
  784. if ( is_string( $default ) )
  785. $default = sprintf( $default, get_template_directory_uri(), get_stylesheet_directory_uri() );
  786. /** This filter is documented in wp-includes/theme.php */
  787. return apply_filters( "theme_mod_{$name}", $default );
  788. }
  789. /**
  790. * Update theme modification value for the current theme.
  791. *
  792. * @since 2.1.0
  793. *
  794. * @param string $name Theme modification name.
  795. * @param mixed $value Theme modification value.
  796. */
  797. function set_theme_mod( $name, $value ) {
  798. $mods = get_theme_mods();
  799. $old_value = isset( $mods[ $name ] ) ? $mods[ $name ] : false;
  800. /**
  801. * Filter the theme mod value on save.
  802. *
  803. * The dynamic portion of the hook name, `$name`, refers to the key name of
  804. * the modification array. For example, 'header_textcolor', 'header_image',
  805. * and so on depending on the theme options.
  806. *
  807. * @since 3.9.0
  808. *
  809. * @param string $value The new value of the theme mod.
  810. * @param string $old_value The current value of the theme mod.
  811. */
  812. $mods[ $name ] = apply_filters( "pre_set_theme_mod_$name", $value, $old_value );
  813. $theme = get_option( 'stylesheet' );
  814. update_option( "theme_mods_$theme", $mods );
  815. }
  816. /**
  817. * Remove theme modification name from current theme list.
  818. *
  819. * If removing the name also removes all elements, then the entire option will
  820. * be removed.
  821. *
  822. * @since 2.1.0
  823. *
  824. * @param string $name Theme modification name.
  825. */
  826. function remove_theme_mod( $name ) {
  827. $mods = get_theme_mods();
  828. if ( ! isset( $mods[ $name ] ) )
  829. return;
  830. unset( $mods[ $name ] );
  831. if ( empty( $mods ) ) {
  832. remove_theme_mods();
  833. return;
  834. }
  835. $theme = get_option( 'stylesheet' );
  836. update_option( "theme_mods_$theme", $mods );
  837. }
  838. /**
  839. * Remove theme modifications option for current theme.
  840. *
  841. * @since 2.1.0
  842. */
  843. function remove_theme_mods() {
  844. delete_option( 'theme_mods_' . get_option( 'stylesheet' ) );
  845. // Old style.
  846. $theme_name = get_option( 'current_theme' );
  847. if ( false === $theme_name )
  848. $theme_name = wp_get_theme()->get('Name');
  849. delete_option( 'mods_' . $theme_name );
  850. }
  851. /**
  852. * Retrieve text color for custom header.
  853. *
  854. * @since 2.1.0
  855. *
  856. * @return string
  857. */
  858. function get_header_textcolor() {
  859. return get_theme_mod('header_textcolor', get_theme_support( 'custom-header', 'default-text-color' ) );
  860. }
  861. /**
  862. * Display text color for custom header.
  863. *
  864. * @since 2.1.0
  865. */
  866. function header_textcolor() {
  867. echo get_header_textcolor();
  868. }
  869. /**
  870. * Whether to display the header text.
  871. *
  872. * @since 3.4.0
  873. *
  874. * @return bool
  875. */
  876. function display_header_text() {
  877. if ( ! current_theme_supports( 'custom-header', 'header-text' ) )
  878. return false;
  879. $text_color = get_theme_mod( 'header_textcolor', get_theme_support( 'custom-header', 'default-text-color' ) );
  880. return 'blank' !== $text_color;
  881. }
  882. /**
  883. * Check whether a header image is set or not.
  884. *
  885. * @since 4.2.0
  886. *
  887. * @see get_header_image()
  888. *
  889. * @return bool Whether a header image is set or not.
  890. */
  891. function has_header_image() {
  892. return (bool) get_header_image();
  893. }
  894. /**
  895. * Retrieve header image for custom header.
  896. *
  897. * @since 2.1.0
  898. *
  899. * @return string|false
  900. */
  901. function get_header_image() {
  902. $url = get_theme_mod( 'header_image', get_theme_support( 'custom-header', 'default-image' ) );
  903. if ( 'remove-header' == $url )
  904. return false;
  905. if ( is_random_header_image() )
  906. $url = get_random_header_image();
  907. return esc_url_raw( set_url_scheme( $url ) );
  908. }
  909. /**
  910. * Create image tag markup for a custom header image.
  911. *
  912. * @since 4.4.0
  913. *
  914. * @param array $attr Optional. Additional attributes for the image tag. Can be used
  915. * to override the default attributes. Default empty.
  916. * @return string HTML image element markup or empty string on failure.
  917. */
  918. function get_header_image_tag( $attr = array() ) {
  919. $header = get_custom_header();
  920. if ( empty( $header->url ) ) {
  921. return '';
  922. }
  923. $width = absint( $header->width );
  924. $height = absint( $header->height );
  925. $attr = wp_parse_args(
  926. $attr,
  927. array(
  928. 'src' => $header->url,
  929. 'width' => $width,
  930. 'height' => $height,
  931. 'alt' => get_bloginfo( 'name' ),
  932. )
  933. );
  934. // Generate 'srcset' and 'sizes' if not already present.
  935. if ( empty( $attr['srcset'] ) && ! empty( $header->attachment_id ) ) {
  936. $image_meta = get_post_meta( $header->attachment_id, '_wp_attachment_metadata', true );
  937. $size_array = array( $width, $height );
  938. if ( is_array( $image_meta ) ) {
  939. $srcset = wp_calculate_image_srcset( $size_array, $header->url, $image_meta, $header->attachment_id );
  940. $sizes = ! empty( $attr['sizes'] ) ? $attr['sizes'] : wp_calculate_image_sizes( $size_array, $header->url, $image_meta, $header->attachment_id );
  941. if ( $srcset && $sizes ) {
  942. $attr['srcset'] = $srcset;
  943. $attr['sizes'] = $sizes;
  944. }
  945. }
  946. }
  947. $attr = array_map( 'esc_attr', $attr );
  948. $html = '<img';
  949. foreach ( $attr as $name => $value ) {
  950. $html .= ' ' . $name . '="' . $value . '"';
  951. }
  952. $html .= ' />';
  953. /**
  954. * Filter the markup of header images.
  955. *
  956. * @since 4.4.0
  957. *
  958. * @param string $html The HTML image tag markup being filtered.
  959. * @param object $header The custom header object returned by 'get_custom_header()'.
  960. * @param array $attr Array of the attributes for the image tag.
  961. */
  962. return apply_filters( 'get_header_image_tag', $html, $header, $attr );
  963. }
  964. /**
  965. * Display the image markup for a custom header image.
  966. *
  967. * @since 4.4.0
  968. *
  969. * @param array $attr Optional. Attributes for the image markup. Default empty.
  970. */
  971. function the_header_image_tag( $attr = array() ) {
  972. echo get_header_image_tag( $attr );
  973. }
  974. /**
  975. * Get random header image data from registered images in theme.
  976. *
  977. * @since 3.4.0
  978. *
  979. * @access private
  980. *
  981. * @global array $_wp_default_headers
  982. * @staticvar object $_wp_random_header
  983. *
  984. * @return object
  985. */
  986. function _get_random_header_data() {
  987. static $_wp_random_header = null;
  988. if ( empty( $_wp_random_header ) ) {
  989. global $_wp_default_headers;
  990. $header_image_mod = get_theme_mod( 'header_image', '' );
  991. $headers = array();
  992. if ( 'random-uploaded-image' == $header_image_mod )
  993. $headers = get_uploaded_header_images();
  994. elseif ( ! empty( $_wp_default_headers ) ) {
  995. if ( 'random-default-image' == $header_image_mod ) {
  996. $headers = $_wp_default_headers;
  997. } else {
  998. if ( current_theme_supports( 'custom-header', 'random-default' ) )
  999. $headers = $_wp_default_headers;
  1000. }
  1001. }
  1002. if ( empty( $headers ) )
  1003. return new stdClass;
  1004. $_wp_random_header = (object) $headers[ array_rand( $headers ) ];
  1005. $_wp_random_header->url = sprintf( $_wp_random_header->url, get_template_directory_uri(), get_stylesheet_directory_uri() );
  1006. $_wp_random_header->thumbnail_url = sprintf( $_wp_random_header->thumbnail_url, get_template_directory_uri(), get_stylesheet_directory_uri() );
  1007. }
  1008. return $_wp_random_header;
  1009. }
  1010. /**
  1011. * Get random header image url from registered images in theme.
  1012. *
  1013. * @since 3.2.0
  1014. *
  1015. * @return string Path to header image
  1016. */
  1017. function get_random_header_image() {
  1018. $random_image = _get_random_header_data();
  1019. if ( empty( $random_image->url ) )
  1020. return '';
  1021. return $random_image->url;
  1022. }
  1023. /**
  1024. * Check if random header image is in use.
  1025. *
  1026. * Always true if user expressly chooses the option in Appearance > Header.
  1027. * Also true if theme has multiple header images registered, no specific header image
  1028. * is chosen, and theme turns on random headers with add_theme_support().
  1029. *
  1030. * @since 3.2.0
  1031. *
  1032. * @param string $type The random pool to use. any|default|uploaded
  1033. * @return bool
  1034. */
  1035. function is_random_header_image( $type = 'any' ) {
  1036. $header_image_mod = get_theme_mod( 'header_image', get_theme_support( 'custom-header', 'default-image' ) );
  1037. if ( 'any' == $type ) {
  1038. if ( 'random-default-image' == $header_image_mod || 'random-uploaded-image' == $header_image_mod || ( '' != get_random_header_image() && empty( $header_image_mod ) ) )
  1039. return true;
  1040. } else {
  1041. if ( "random-$type-image" == $header_image_mod )
  1042. return true;
  1043. elseif ( 'default' == $type && empty( $header_image_mod ) && '' != get_random_header_image() )
  1044. return true;
  1045. }
  1046. return false;
  1047. }
  1048. /**
  1049. * Display header image URL.
  1050. *
  1051. * @since 2.1.0
  1052. */
  1053. function header_image() {
  1054. $image = get_header_image();
  1055. if ( $image ) {
  1056. echo esc_url( $image );
  1057. }
  1058. }
  1059. /**
  1060. * Get the header images uploaded for the current theme.
  1061. *
  1062. * @since 3.2.0
  1063. *
  1064. * @return array
  1065. */
  1066. function get_uploaded_header_images() {
  1067. $header_images = array();
  1068. // @todo caching
  1069. $headers = get_posts( array( 'post_type' => 'attachment', 'meta_key' => '_wp_attachment_is_custom_header', 'meta_value' => get_option('stylesheet'), 'orderby' => 'none', 'nopaging' => true ) );
  1070. if ( empty( $headers ) )
  1071. return array();
  1072. foreach ( (array) $headers as $header ) {
  1073. $url = esc_url_raw( wp_get_attachment_url( $header->ID ) );
  1074. $header_data = wp_get_attachment_metadata( $header->ID );
  1075. $header_index = basename($url);
  1076. $header_images[$header_index] = array();
  1077. $header_images[$header_index]['attachment_id'] = $header->ID;
  1078. $header_images[$header_index]['url'] = $url;
  1079. $header_images[$header_index]['thumbnail_url'] = $url;
  1080. $header_images[$header_index]['alt_text'] = get_post_meta( $header->ID, '_wp_attachment_image_alt', true );
  1081. if ( isset( $header_data['width'] ) )
  1082. $header_images[$header_index]['width'] = $header_data['width'];
  1083. if ( isset( $header_data['height'] ) )
  1084. $header_images[$header_index]['height'] = $header_data['height'];
  1085. }
  1086. return $header_images;
  1087. }
  1088. /**
  1089. * Get the header image data.
  1090. *
  1091. * @since 3.4.0
  1092. *
  1093. * @global array $_wp_default_headers
  1094. *
  1095. * @return object
  1096. */
  1097. function get_custom_header() {
  1098. global $_wp_default_headers;
  1099. if ( is_random_header_image() ) {
  1100. $data = _get_random_header_data();
  1101. } else {
  1102. $data = get_theme_mod( 'header_image_data' );
  1103. if ( ! $data && current_theme_supports( 'custom-header', 'default-image' ) ) {
  1104. $directory_args = array( get_template_directory_uri(), get_stylesheet_directory_uri() );
  1105. $data = array();
  1106. $data['url'] = $data['thumbnail_url'] = vsprintf( get_theme_support( 'custom-header', 'default-image' ), $directory_args );
  1107. if ( ! empty( $_wp_default_headers ) ) {
  1108. foreach ( (array) $_wp_default_headers as $default_header ) {
  1109. $url = vsprintf( $default_header['url'], $directory_args );
  1110. if ( $data['url'] == $url ) {
  1111. $data = $default_header;
  1112. $data['url'] = $url;
  1113. $data['thumbnail_url'] = vsprintf( $data['thumbnail_url'], $directory_args );
  1114. break;
  1115. }
  1116. }
  1117. }
  1118. }
  1119. }
  1120. $default = array(
  1121. 'url' => '',
  1122. 'thumbnail_url' => '',
  1123. 'width' => get_theme_support( 'custom-header', 'width' ),
  1124. 'height' => get_theme_support( 'custom-header', 'height' ),
  1125. );
  1126. return (object) wp_parse_args( $data, $default );
  1127. }
  1128. /**
  1129. * Register a selection of default headers to be displayed by the custom header admin UI.
  1130. *
  1131. * @since 3.0.0
  1132. *
  1133. * @global array $_wp_default_headers
  1134. *
  1135. * @param array $headers Array of headers keyed by a string id. The ids point to arrays containing 'url', 'thumbnail_url', and 'description' keys.
  1136. */
  1137. function register_default_headers( $headers ) {
  1138. global $_wp_default_headers;
  1139. $_wp_default_headers = array_merge( (array) $_wp_default_headers, (array) $headers );
  1140. }
  1141. /**
  1142. * Unregister default headers.
  1143. *
  1144. * This function must be called after register_default_headers() has already added the
  1145. * header you want to remove.
  1146. *
  1147. * @see register_default_headers()
  1148. * @since 3.0.0
  1149. *
  1150. * @global array $_wp_default_headers
  1151. *
  1152. * @param string|array $header The header string id (key of array) to remove, or an array thereof.
  1153. * @return bool|void A single header returns true on success, false on failure.
  1154. * There is currently no return value for multiple headers.
  1155. */
  1156. function unregister_default_headers( $header ) {
  1157. global $_wp_default_headers;
  1158. if ( is_array( $header ) ) {
  1159. array_map( 'unregister_default_headers', $header );
  1160. } elseif ( isset( $_wp_default_headers[ $header ] ) ) {
  1161. unset( $_wp_default_headers[ $header ] );
  1162. return true;
  1163. } else {
  1164. return false;
  1165. }
  1166. }
  1167. /**
  1168. * Retrieve background image for custom background.
  1169. *
  1170. * @since 3.0.0
  1171. *
  1172. * @return string
  1173. */
  1174. function get_background_image() {
  1175. return get_theme_mod('background_image', get_theme_support( 'custom-background', 'default-image' ) );
  1176. }
  1177. /**
  1178. * Display background image path.
  1179. *
  1180. * @since 3.0.0
  1181. */
  1182. function background_image() {
  1183. echo get_background_image();
  1184. }
  1185. /**
  1186. * Retrieve value for custom background color.
  1187. *
  1188. * @since 3.0.0
  1189. *
  1190. * @return string
  1191. */
  1192. function get_background_color() {
  1193. return get_theme_mod('background_color', get_theme_support( 'custom-background', 'default-color' ) );
  1194. }
  1195. /**
  1196. * Display background color value.
  1197. *
  1198. * @since 3.0.0
  1199. */
  1200. function background_color() {
  1201. echo get_background_color();
  1202. }
  1203. /**
  1204. * Default custom background callback.
  1205. *
  1206. * @since 3.0.0
  1207. * @access protected
  1208. */
  1209. function _custom_background_cb() {
  1210. // $background is the saved custom image, or the default image.
  1211. $background = set_url_scheme( get_background_image() );
  1212. // $color is the saved custom color.
  1213. // A default has to be specified in style.css. It will not be printed here.
  1214. $color = get_background_color();
  1215. if ( $color === get_theme_support( 'custom-background', 'default-color' ) ) {
  1216. $color = false;
  1217. }
  1218. if ( ! $background && ! $color )
  1219. return;
  1220. $style = $color ? "background-color: #$color;" : '';
  1221. if ( $background ) {
  1222. $image = " background-image: url('$background');";
  1223. $repeat = get_theme_mod( 'background_repeat', get_theme_support( 'custom-background', 'default-repeat' ) );
  1224. if ( ! in_array( $repeat, array( 'no-repeat', 'repeat-x', 'repeat-y', 'repeat' ) ) )
  1225. $repeat = 'repeat';
  1226. $repeat = " background-repeat: $repeat;";
  1227. $position = get_theme_mod( 'background_position_x', get_theme_support( 'custom-background', 'default-position-x' ) );
  1228. if ( ! in_array( $position, array( 'center', 'right', 'left' ) ) )
  1229. $position = 'left';
  1230. $position = " background-position: top $position;";
  1231. $attachment = get_theme_mod( 'background_attachment', get_theme_support( 'custom-background', 'default-attachment' ) );
  1232. if ( ! in_array( $attachment, array( 'fixed', 'scroll' ) ) )
  1233. $attachment = 'scroll';
  1234. $attachment = " background-attachment: $attachment;";
  1235. $style .= $image . $repeat . $position . $attachment;
  1236. }
  1237. ?>
  1238. <style type="text/css" id="custom-background-css">
  1239. body.custom-background { <?php echo trim( $style ); ?> }
  1240. </style>
  1241. <?php
  1242. }
  1243. /**
  1244. * Add callback for custom TinyMCE editor stylesheets.
  1245. *
  1246. * The parameter $stylesheet is the name of the stylesheet, relative to
  1247. * the theme root. It also accepts an array of stylesheets.
  1248. * It is optional and defaults to 'editor-style.css'.
  1249. *
  1250. * This function automatically adds another stylesheet with -rtl prefix, e.g. editor-style-rtl.css.
  1251. * If that file doesn't exist, it is removed before adding the stylesheet(s) to TinyMCE.
  1252. * If an array of stylesheets is passed to add_editor_style(),
  1253. * RTL is only added for the first stylesheet.
  1254. *
  1255. * Since version 3.4 the TinyMCE body has .rtl CSS class.
  1256. * It is a better option to use that class and add any RTL styles to the main stylesheet.
  1257. *
  1258. * @since 3.0.0
  1259. *
  1260. * @global array $editor_styles
  1261. *
  1262. * @param array|string $stylesheet Optional. Stylesheet name or array thereof, relative to theme root.
  1263. * Defaults to 'editor-style.css'
  1264. */
  1265. function add_editor_style( $stylesheet = 'editor-style.css' ) {
  1266. add_theme_support( 'editor-style' );
  1267. if ( ! is_admin() )
  1268. return;
  1269. global $editor_styles;
  1270. $editor_styles = (array) $editor_styles;
  1271. $stylesheet = (array) $stylesheet;
  1272. if ( is_rtl() ) {
  1273. $rtl_stylesheet = str_replace('.css', '-rtl.css', $stylesheet[0]);
  1274. $stylesheet[] = $rtl_stylesheet;
  1275. }
  1276. $editor_styles = array_merge( $editor_styles, $stylesheet );
  1277. }
  1278. /**
  1279. * Removes all visual editor stylesheets.
  1280. *
  1281. * @since 3.1.0
  1282. *
  1283. * @global array $editor_styles
  1284. *
  1285. * @return bool True on success, false if there were no stylesheets to remove.
  1286. */
  1287. function remove_editor_styles() {
  1288. if ( ! current_theme_supports( 'editor-style' ) )
  1289. return false;
  1290. _remove_theme_support( 'editor-style' );
  1291. if ( is_admin() )
  1292. $GLOBALS['editor_styles'] = array();
  1293. return true;
  1294. }
  1295. /**
  1296. * Retrieve any registered editor stylesheets
  1297. *
  1298. * @since 4.0.0
  1299. *
  1300. * @global array $editor_styles Registered editor stylesheets
  1301. *
  1302. * @return array If registered, a list of editor stylesheet URLs.
  1303. */
  1304. function get_editor_stylesheets() {
  1305. $stylesheets = array();
  1306. // load editor_style.css if the current theme supports it
  1307. if ( ! empty( $GLOBALS['editor_styles'] ) && is_array( $GLOBALS['editor_styles'] ) ) {
  1308. $editor_styles = $GLOBALS['editor_styles'];
  1309. $editor_styles = array_unique( array_filter( $editor_styles ) );
  1310. $style_uri = get_stylesheet_directory_uri();
  1311. $style_dir = get_stylesheet_directory();
  1312. // Support externally referenced styles (like, say, fonts).
  1313. foreach ( $editor_styles as $key => $file ) {
  1314. if ( preg_match( '~^(https?:)?//~', $file ) ) {
  1315. $stylesheets[] = esc_url_raw( $file );
  1316. unset( $editor_styles[ $key ] );
  1317. }
  1318. }
  1319. // Look in a parent theme first, that way child theme CSS overrides.
  1320. if ( is_child_theme() ) {
  1321. $template_uri = get_template_directory_uri();
  1322. $template_dir = get_template_directory();
  1323. foreach ( $editor_styles as $key => $file ) {
  1324. if ( $file && file_exists( "$template_dir/$file" ) ) {
  1325. $stylesheets[] = "$template_uri/$file";
  1326. }
  1327. }
  1328. }
  1329. foreach ( $editor_styles as $file ) {
  1330. if ( $file && file_exists( "$style_dir/$file" ) ) {
  1331. $stylesheets[] = "$style_uri/$file";
  1332. }
  1333. }
  1334. }
  1335. /**
  1336. * Filter the array of stylesheets applied to the editor.
  1337. *
  1338. * @since 4.3.0
  1339. *
  1340. * @param array $stylesheets Array of stylesheets to be applied to the editor.
  1341. */
  1342. return apply_filters( 'editor_stylesheets', $stylesheets );
  1343. }
  1344. /**
  1345. * Allows a theme to register its support of a certain feature
  1346. *
  1347. * Must be called in the theme's functions.php file to work.
  1348. * If attached to a hook, it must be after_setup_theme.
  1349. * The init hook may be too late for some features.
  1350. *
  1351. * @since 2.9.0
  1352. *
  1353. * @global array $_wp_theme_features
  1354. *
  1355. * @param string $feature The feature being added.
  1356. * @return void|bool False on failure, void otherwise.
  1357. */
  1358. function add_theme_support( $feature ) {
  1359. global $_wp_theme_features;
  1360. if ( func_num_args() == 1 )
  1361. $args = true;
  1362. else
  1363. $args = array_slice( func_get_args(), 1 );
  1364. switch ( $feature ) {
  1365. case 'post-formats' :
  1366. if ( is_array( $args[0] ) ) {
  1367. $post_formats = get_post_format_slugs();
  1368. unset( $post_formats['standard'] );
  1369. $args[0] = array_intersect( $args[0], array_keys( $post_formats ) );
  1370. }
  1371. break;
  1372. case 'html5' :
  1373. // You can't just pass 'html5', you need to pass an array of types.
  1374. if ( empty( $args[0] ) ) {
  1375. // Build an array of types for back-compat.
  1376. $args = array( 0 => array( 'comment-list', 'comment-form', 'search-form' ) );
  1377. } elseif ( ! is_array( $args[0] ) ) {
  1378. _doing_it_wrong( "add_theme_support( 'html5' )", __( 'You need to pass an array of types.' ), '3.6.1' );
  1379. return false;
  1380. }
  1381. // Calling 'html5' again merges, rather than overwrites.
  1382. if ( isset( $_wp_theme_features['html5'] ) )
  1383. $args[0] = array_merge( $_wp_theme_features['html5'][0], $args[0] );
  1384. break;
  1385. case 'custom-header-uploads' :
  1386. return add_theme_support( 'custom-header', array( 'uploads' => true ) );
  1387. case 'custom-header' :
  1388. if ( ! is_array( $args ) )
  1389. $args = array( 0 => array() );
  1390. $defaults = array(
  1391. 'default-image' => '',
  1392. 'random-default' => false,
  1393. 'width' => 0,
  1394. 'height' => 0,
  1395. 'flex-height' => false,
  1396. 'flex-width' => false,
  1397. 'default-text-color' => '',
  1398. 'header-text' => true,
  1399. 'uploads' => true,
  1400. 'wp-head-callback' => '',
  1401. 'admin-head-callback' => '',
  1402. 'admin-preview-callback' => '',
  1403. );
  1404. $jit = isset( $args[0]['__jit'] );
  1405. unset( $args[0]['__jit'] );
  1406. // Merge in data from previous add_theme_support() calls.
  1407. // The first value registered wins. (A child theme is set up first.)
  1408. if ( isset( $_wp_theme_features['custom-header'] ) )
  1409. $args[0] = wp_parse_args( $_wp_theme_features['custom-header'][0], $args[0] );
  1410. // Load in the defaults at the end, as we need to insure first one wins.
  1411. // This will cause all constants to be defined, as each arg will then be set to the default.
  1412. if ( $jit )
  1413. $args[0] = wp_parse_args( $args[0], $defaults );
  1414. // If a constant was defined, use that value. Otherwise, define the constant to ensure
  1415. // the constant is always accurate (and is not defined later, overriding our value).
  1416. // As stated above, the first value wins.
  1417. // Once we get to wp_loaded (just-in-time), define any constants we haven't already.
  1418. // Constants are lame. Don't reference them. This is just for backwards compatibility.
  1419. if ( defined( 'NO_HEADER_TEXT' ) )
  1420. $args[0]['header-text'] = ! NO_HEADER_TEXT;
  1421. elseif ( isset( $args[0]['header-text'] ) )
  1422. define( 'NO_HEADER_TEXT', empty( $args[0]['header-text'] ) );
  1423. if ( defined( 'HEADER_IMAGE_WIDTH' ) )
  1424. $args[0]['width'] = (int) HEADER_IMAGE_WIDTH;
  1425. elseif ( isset( $args[0]['width'] ) )
  1426. define( 'HEADER_IMAGE_WIDTH', (int) $args[0]['width'] );
  1427. if ( defined( 'HEADER_IMAGE_HEIGHT' ) )
  1428. $args[0]['height'] = (int) HEADER_IMAGE_HEIGHT;
  1429. elseif ( isset( $args[0]['height'] ) )
  1430. define( 'HEADER_IMAGE_HEIGHT', (int) $args[0]['height'] );
  1431. if ( defined( 'HEADER_TEXTCOLOR' ) )
  1432. $args[0]['default-text-color'] = HEADER_TEXTCOLOR;
  1433. elseif ( isset( $args[0]['default-text-color'] ) )
  1434. define( 'HEADER_TEXTCOLOR', $args[0]['default-text-color'] );
  1435. if ( defined( 'HEADER_IMAGE' ) )
  1436. $args[0]['default-image'] = HEADER_IMAGE;
  1437. elseif ( isset( $args[0]['default-image'] ) )
  1438. define( 'HEADER_IMAGE', $args[0]['default-image'] );
  1439. if ( $jit && ! empty( $args[0]['default-image'] ) )
  1440. $args[0]['random-default'] = false;
  1441. // If headers are supported, and we still don't have a defined width or height,
  1442. // we have implicit flex sizes.
  1443. if ( $jit ) {
  1444. if ( empty( $args[0]['width'] ) && empty( $args[0]['flex-width'] ) )
  1445. $args[0]['flex-width'] = true;