PageRenderTime 37ms CodeModel.GetById 1ms RepoModel.GetById 1ms app.codeStats 0ms

/wp-includes/theme.php

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