at path:ROOT / wp-includes / theme.php
run:R W Run
DIR
2026-03-11 16:18:51
R W Run
DIR
2026-03-11 16:18:52
R W Run
DIR
2026-03-11 16:18:51
R W Run
DIR
2026-03-11 16:18:52
R W Run
DIR
2026-03-11 16:18:52
R W Run
DIR
2026-03-11 16:18:51
R W Run
DIR
2026-03-11 16:18:51
R W Run
DIR
2026-03-11 16:18:51
R W Run
DIR
2026-03-11 16:18:51
R W Run
DIR
2026-03-11 16:18:52
R W Run
DIR
2026-03-11 16:18:52
R W Run
DIR
2026-03-11 16:18:52
R W Run
DIR
2026-03-11 16:18:51
R W Run
DIR
2026-03-11 16:18:52
R W Run
DIR
2026-03-11 16:18:52
R W Run
DIR
2026-03-11 16:18:51
R W Run
DIR
2026-03-11 16:18:52
R W Run
DIR
2026-03-11 16:18:51
R W Run
DIR
2026-03-11 16:18:51
R W Run
DIR
2026-03-11 16:18:51
R W Run
DIR
2026-03-11 16:18:52
R W Run
DIR
2026-03-11 16:18:52
R W Run
DIR
2026-03-11 16:18:52
R W Run
DIR
2026-03-11 16:18:52
R W Run
DIR
2026-03-11 16:18:51
R W Run
DIR
2026-03-11 16:18:52
R W Run
DIR
2026-03-11 16:18:52
R W Run
DIR
2026-03-11 16:18:51
R W Run
DIR
2026-03-11 16:18:51
R W Run
23.8 KB
2026-03-11 16:18:51
R W Run
7.8 KB
2026-03-11 16:18:52
R W Run
36.1 KB
2026-03-11 16:18:51
R W Run
11.9 KB
2026-03-11 16:18:52
R W Run
18.94 KB
2026-03-11 16:18:52
R W Run
7.35 KB
2026-03-11 16:18:52
R W Run
28.6 KB
2026-03-11 16:18:51
R W Run
316 By
2026-03-11 16:18:51
R W Run
12.9 KB
2026-03-11 16:18:51
R W Run
61.02 KB
2026-03-11 16:18:52
R W Run
15 KB
2026-03-11 16:18:51
R W Run
112.05 KB
2026-03-11 16:18:51
R W Run
12.47 KB
2026-03-11 16:18:51
R W Run
15.07 KB
2026-03-11 16:18:52
R W Run
9.84 KB
2026-03-11 16:18:52
R W Run
13.17 KB
2026-03-11 16:18:52
R W Run
33.83 KB
2026-03-11 16:18:51
R W Run
42.63 KB
2026-03-11 16:18:51
R W Run
55.71 KB
2026-03-11 16:18:52
R W Run
12.53 KB
2026-03-11 16:18:51
R W Run
2.55 KB
2026-03-11 16:18:52
R W Run
28.92 KB
2026-03-11 16:18:52
R W Run
539 By
2026-03-11 16:18:51
R W Run
367 By
2026-03-11 16:18:52
R W Run
42.65 KB
2026-03-11 16:18:51
R W Run
401 By
2026-03-11 16:18:51
R W Run
6.61 KB
2026-03-11 16:18:51
R W Run
664 By
2026-03-11 16:18:52
R W Run
20.63 KB
2026-03-11 16:18:51
R W Run
2.18 KB
2026-03-11 16:18:52
R W Run
453 By
2026-03-11 16:18:52
R W Run
457 By
2026-03-11 16:18:51
R W Run
36.83 KB
2026-03-11 16:18:52
R W Run
2.41 KB
2026-03-11 16:18:52
R W Run
8.28 KB
2026-03-11 16:18:51
R W Run
13.89 KB
2026-03-11 16:18:51
R W Run
11.76 KB
2026-03-11 16:18:51
R W Run
2.65 KB
2026-03-11 16:18:51
R W Run
7.43 KB
2026-03-11 16:18:51
R W Run
17.46 KB
2026-03-11 16:18:51
R W Run
5.14 KB
2026-03-11 16:18:52
R W Run
16.7 KB
2026-03-11 16:18:51
R W Run
8.28 KB
2026-03-11 16:18:52
R W Run
2.92 KB
2026-03-11 16:18:52
R W Run
1.32 KB
2026-03-11 16:18:51
R W Run
4.6 KB
2026-03-11 16:18:52
R W Run
11.62 KB
2026-03-11 16:18:52
R W Run
2.5 KB
2026-03-11 16:18:51
R W Run
1.97 KB
2026-03-11 16:18:51
R W Run
11.25 KB
2026-03-11 16:18:52
R W Run
5.32 KB
2026-03-11 16:18:51
R W Run
10.99 KB
2026-03-11 16:18:52
R W Run
68.32 KB
2026-03-11 16:18:51
R W Run
6.34 KB
2026-03-11 16:18:51
R W Run
5.49 KB
2026-03-11 16:18:51
R W Run
1.99 KB
2026-03-11 16:18:52
R W Run
7.02 KB
2026-03-11 16:18:51
R W Run
4.91 KB
2026-03-11 16:18:52
R W Run
16.86 KB
2026-03-11 16:18:51
R W Run
24.23 KB
2026-03-11 16:18:51
R W Run
3.97 KB
2026-03-11 16:18:51
R W Run
47.66 KB
2026-03-11 16:18:51
R W Run
9.22 KB
2026-03-11 16:18:51
R W Run
25.51 KB
2026-03-11 16:18:51
R W Run
198.38 KB
2026-03-11 16:18:52
R W Run
56.65 KB
2026-03-11 16:18:51
R W Run
10.46 KB
2026-03-11 16:18:51
R W Run
10.95 KB
2026-03-11 16:18:52
R W Run
29.26 KB
2026-03-11 16:18:51
R W Run
70.91 KB
2026-03-11 16:18:52
R W Run
35.3 KB
2026-03-11 16:18:52
R W Run
16.61 KB
2026-03-11 16:18:52
R W Run
2.57 KB
2026-03-11 16:18:52
R W Run
39.83 KB
2026-03-11 16:18:51
R W Run
70.64 KB
2026-03-11 16:18:51
R W Run
15.56 KB
2026-03-11 16:18:52
R W Run
7.33 KB
2026-03-11 16:18:52
R W Run
253 By
2026-03-11 16:18:51
R W Run
7.96 KB
2026-03-11 16:18:52
R W Run
3.23 KB
2026-03-11 16:18:52
R W Run
969 By
2026-03-11 16:18:52
R W Run
16.28 KB
2026-03-11 16:18:51
R W Run
7.22 KB
2026-03-11 16:18:51
R W Run
12.95 KB
2026-03-11 16:18:51
R W Run
6.53 KB
2026-03-11 16:18:51
R W Run
3.42 KB
2026-03-11 16:18:52
R W Run
5.84 KB
2026-03-11 16:18:51
R W Run
1.97 KB
2026-03-11 16:18:51
R W Run
4.3 KB
2026-03-11 16:18:52
R W Run
2.91 KB
2026-03-11 16:18:51
R W Run
16.46 KB
2026-03-11 16:18:52
R W Run
40.6 KB
2026-03-11 16:18:51
R W Run
20.22 KB
2026-03-11 16:18:51
R W Run
36.11 KB
2026-03-11 16:18:52
R W Run
17.01 KB
2026-03-11 16:18:51
R W Run
7.27 KB
2026-03-11 16:18:52
R W Run
6.62 KB
2026-03-11 16:18:52
R W Run
16.49 KB
2026-03-11 16:18:52
R W Run
1.79 KB
2026-03-11 16:18:52
R W Run
29.82 KB
2026-03-11 16:18:51
R W Run
6.67 KB
2026-03-11 16:18:52
R W Run
8.98 KB
2026-03-11 16:18:52
R W Run
19.42 KB
2026-03-11 16:18:51
R W Run
12.01 KB
2026-03-11 16:18:51
R W Run
17.11 KB
2026-03-11 16:18:51
R W Run
6.74 KB
2026-03-11 16:18:52
R W Run
30.93 KB
2026-03-11 16:18:51
R W Run
4.99 KB
2026-03-11 16:18:51
R W Run
4.25 KB
2026-03-11 16:18:51
R W Run
24.72 KB
2026-03-11 16:18:51
R W Run
29.96 KB
2026-03-11 16:18:52
R W Run
6.41 KB
2026-03-11 16:18:51
R W Run
160 KB
2026-03-11 16:18:51
R W Run
6.72 KB
2026-03-11 16:18:52
R W Run
10.92 KB
2026-03-11 16:18:51
R W Run
4.77 KB
2026-03-11 16:18:51
R W Run
3.38 KB
2026-03-11 16:18:51
R W Run
11.18 KB
2026-03-11 16:18:51
R W Run
62.19 KB
2026-03-11 16:18:51
R W Run
2.46 KB
2026-03-11 16:18:51
R W Run
9.17 KB
2026-03-11 16:18:51
R W Run
32.15 KB
2026-03-11 16:18:51
R W Run
34.05 KB
2026-03-11 16:18:52
R W Run
7.15 KB
2026-03-11 16:18:51
R W Run
3.47 KB
2026-03-11 16:18:52
R W Run
1.87 KB
2026-03-11 16:18:52
R W Run
30.91 KB
2026-03-11 16:18:51
R W Run
7.29 KB
2026-03-11 16:18:52
R W Run
7.35 KB
2026-03-11 16:18:51
R W Run
12.54 KB
2026-03-11 16:18:51
R W Run
19.12 KB
2026-03-11 16:18:51
R W Run
18.12 KB
2026-03-11 16:18:52
R W Run
39.99 KB
2026-03-11 16:18:52
R W Run
5.17 KB
2026-03-11 16:18:52
R W Run
979 By
2026-03-11 16:18:51
R W Run
18.44 KB
2026-03-11 16:18:52
R W Run
10.24 KB
2026-03-11 16:18:51
R W Run
1.77 KB
2026-03-11 16:18:52
R W Run
34.9 KB
2026-03-11 16:18:51
R W Run
7.19 KB
2026-03-11 16:18:52
R W Run
160.5 KB
2026-03-11 16:18:51
R W Run
64.27 KB
2026-03-11 16:18:51
R W Run
27.95 KB
2026-03-11 16:18:51
R W Run
4.69 KB
2026-03-11 16:18:51
R W Run
2.94 KB
2026-03-11 16:18:51
R W Run
43.13 KB
2026-03-11 16:18:52
R W Run
2.25 KB
2026-03-11 16:18:52
R W Run
22.5 KB
2026-03-11 16:18:51
R W Run
13.01 KB
2026-03-11 16:18:52
R W Run
3.27 KB
2026-03-11 16:18:51
R W Run
18 KB
2026-03-11 16:18:51
R W Run
210.4 KB
2026-03-11 16:18:52
R W Run
25.86 KB
2026-03-11 16:18:52
R W Run
115.85 KB
2026-03-11 16:18:51
R W Run
373 By
2026-03-11 16:18:52
R W Run
343 By
2026-03-11 16:18:52
R W Run
338 By
2026-03-11 16:18:51
R W Run
100.73 KB
2026-03-11 16:18:52
R W Run
130.93 KB
2026-03-11 16:18:51
R W Run
19.1 KB
2026-03-11 16:18:51
R W Run
17.41 KB
2026-03-11 16:18:52
R W Run
41.98 KB
2026-03-11 16:18:52
R W Run
400 By
2026-03-11 16:18:52
R W Run
11.1 KB
2026-03-11 16:18:52
R W Run
37.02 KB
2026-03-11 16:18:51
R W Run
2.24 KB
2026-03-11 16:18:51
R W Run
188.13 KB
2026-03-11 16:18:51
R W Run
338 By
2026-03-11 16:18:51
R W Run
38 KB
2026-03-11 16:18:51
R W Run
4.02 KB
2026-03-11 16:18:52
R W Run
5.38 KB
2026-03-11 16:18:51
R W Run
3.05 KB
2026-03-11 16:18:52
R W Run
2.61 KB
2026-03-11 16:18:51
R W Run
1.16 KB
2026-03-11 16:18:52
R W Run
4.04 KB
2026-03-11 16:18:51
R W Run
3.71 KB
2026-03-11 16:18:51
R W Run
24.6 KB
2026-03-11 16:18:51
R W Run
9.56 KB
2026-03-11 16:18:51
R W Run
346.43 KB
2026-03-11 16:18:52
R W Run
281.84 KB
2026-03-11 16:18:52
R W Run
14.95 KB
2026-03-11 16:18:51
R W Run
8.44 KB
2026-03-11 16:18:52
R W Run
168.95 KB
2026-03-11 16:18:52
R W Run
20.71 KB
2026-03-11 16:18:52
R W Run
25.27 KB
2026-03-11 16:18:51
R W Run
5.72 KB
2026-03-11 16:18:51
R W Run
4.63 KB
2026-03-11 16:18:52
R W Run
81.73 KB
2026-03-11 16:18:51
R W Run
67.18 KB
2026-03-11 16:18:51
R W Run
156.36 KB
2026-03-11 16:18:52
R W Run
55.19 KB
2026-03-11 16:18:51
R W Run
162 By
2026-03-11 16:18:51
R W Run
61.72 KB
2026-03-11 16:18:51
R W Run
216.06 KB
2026-03-11 16:18:52
R W Run
65.09 KB
2026-03-11 16:18:51
R W Run
25.24 KB
2026-03-11 16:18:52
R W Run
4.81 KB
2026-03-11 16:18:51
R W Run
6.48 KB
2026-03-11 16:18:52
R W Run
21.25 KB
2026-03-11 16:18:51
R W Run
2.79 KB
2026-03-11 16:18:52
R W Run
89.69 KB
2026-03-11 16:18:52
R W Run
19.42 KB
2026-03-11 16:18:52
R W Run
3.69 KB
2026-03-11 16:18:52
R W Run
4.11 KB
2026-03-11 16:18:51
R W Run
40.74 KB
2026-03-11 16:18:51
R W Run
25.38 KB
2026-03-11 16:18:51
R W Run
43.31 KB
2026-03-11 16:18:52
R W Run
102.57 KB
2026-03-11 16:18:52
R W Run
6.18 KB
2026-03-11 16:18:51
R W Run
124.47 KB
2026-03-11 16:18:52
R W Run
35.65 KB
2026-03-11 16:18:52
R W Run
6.94 KB
2026-03-11 16:18:52
R W Run
67.04 KB
2026-03-11 16:18:52
R W Run
10.62 KB
2026-03-11 16:18:51
R W Run
289.35 KB
2026-03-11 16:18:52
R W Run
36.23 KB
2026-03-11 16:18:51
R W Run
200 By
2026-03-11 16:18:52
R W Run
200 By
2026-03-11 16:18:52
R W Run
98.29 KB
2026-03-11 16:18:52
R W Run
30.02 KB
2026-03-11 16:18:52
R W Run
19.03 KB
2026-03-11 16:18:52
R W Run
5.06 KB
2026-03-11 16:18:52
R W Run
255 By
2026-03-11 16:18:51
R W Run
22.66 KB
2026-03-11 16:18:52
R W Run
154.63 KB
2026-03-11 16:18:51
R W Run
9.68 KB
2026-03-11 16:18:51
R W Run
258 By
2026-03-11 16:18:51
R W Run
23.49 KB
2026-03-11 16:18:51
R W Run
3.16 KB
2026-03-11 16:18:51
R W Run
8.4 KB
2026-03-11 16:18:52
R W Run
441 By
2026-03-11 16:18:51
R W Run
7.39 KB
2026-03-11 16:18:51
R W Run
173 KB
2026-03-11 16:18:52
R W Run
544 By
2026-03-11 16:18:52
R W Run
4.17 KB
2026-03-11 16:18:51
R W Run
35.97 KB
2026-03-11 16:18:52
R W Run
1.69 KB
2026-03-11 16:18:51
R W Run
2.84 KB
2026-03-11 16:18:52
R W Run
6.09 KB
2026-03-11 16:18:51
R W Run
8.71 KB
2026-03-11 16:18:51
R W Run
131.84 KB
2026-03-11 16:18:51
R W Run
37.45 KB
2026-03-11 16:18:51
R W Run
173.89 KB
2026-03-11 16:18:51
R W Run
7.09 KB
2026-03-11 16:18:51
R W Run
6.41 KB
2026-03-11 16:18:51
R W Run
1.08 KB
2026-03-11 16:18:51
R W Run
69.46 KB
2026-03-11 16:18:52
R W Run
445 By
2026-03-11 16:18:51
R W Run
799 By
2026-03-11 16:18:52
R W Run
error_log
📄theme.php
1<?php
2/**
3 * Theme, template, and stylesheet functions.
4 *
5 * @package WordPress
6 * @subpackage Theme
7 */
8
9/**
10 * Returns an array of WP_Theme objects based on the arguments.
11 *
12 * Despite advances over get_themes(), this function is quite expensive, and grows
13 * linearly with additional themes. Stick to wp_get_theme() if possible.
14 *
15 * @since 3.4.0
16 *
17 * @global string[] $wp_theme_directories
18 *
19 * @param array $args {
20 * Optional. The search arguments.
21 *
22 * @type mixed $errors True to return themes with errors, false to return
23 * themes without errors, null to return all themes.
24 * Default false.
25 * @type mixed $allowed (Multisite) True to return only allowed themes for a site.
26 * False to return only disallowed themes for a site.
27 * 'site' to return only site-allowed themes.
28 * 'network' to return only network-allowed themes.
29 * Null to return all themes. Default null.
30 * @type int $blog_id (Multisite) The blog ID used to calculate which themes
31 * are allowed. Default 0, synonymous for the current blog.
32 * }
33 * @return WP_Theme[] Array of WP_Theme objects.
34 */
35function wp_get_themes( $args = array() ) {
36 global $wp_theme_directories;
37
38 $defaults = array(
39 'errors' => false,
40 'allowed' => null,
41 'blog_id' => 0,
42 );
43 $args = wp_parse_args( $args, $defaults );
44
45 $theme_directories = search_theme_directories();
46
47 if ( is_array( $wp_theme_directories ) && count( $wp_theme_directories ) > 1 ) {
48 /*
49 * Make sure the active theme wins out, in case search_theme_directories() picks the wrong
50 * one in the case of a conflict. (Normally, last registered theme root wins.)
51 */
52 $current_theme = get_stylesheet();
53 if ( isset( $theme_directories[ $current_theme ] ) ) {
54 $root_of_current_theme = get_raw_theme_root( $current_theme );
55 if ( ! in_array( $root_of_current_theme, $wp_theme_directories, true ) ) {
56 $root_of_current_theme = WP_CONTENT_DIR . $root_of_current_theme;
57 }
58 $theme_directories[ $current_theme ]['theme_root'] = $root_of_current_theme;
59 }
60 }
61
62 if ( empty( $theme_directories ) ) {
63 return array();
64 }
65
66 if ( is_multisite() && null !== $args['allowed'] ) {
67 $allowed = $args['allowed'];
68 if ( 'network' === $allowed ) {
69 $theme_directories = array_intersect_key( $theme_directories, WP_Theme::get_allowed_on_network() );
70 } elseif ( 'site' === $allowed ) {
71 $theme_directories = array_intersect_key( $theme_directories, WP_Theme::get_allowed_on_site( $args['blog_id'] ) );
72 } elseif ( $allowed ) {
73 $theme_directories = array_intersect_key( $theme_directories, WP_Theme::get_allowed( $args['blog_id'] ) );
74 } else {
75 $theme_directories = array_diff_key( $theme_directories, WP_Theme::get_allowed( $args['blog_id'] ) );
76 }
77 }
78
79 $themes = array();
80 static $_themes = array();
81
82 foreach ( $theme_directories as $theme => $theme_root ) {
83 if ( isset( $_themes[ $theme_root['theme_root'] . '/' . $theme ] ) ) {
84 $themes[ $theme ] = $_themes[ $theme_root['theme_root'] . '/' . $theme ];
85 } else {
86 $themes[ $theme ] = new WP_Theme( $theme, $theme_root['theme_root'] );
87
88 $_themes[ $theme_root['theme_root'] . '/' . $theme ] = $themes[ $theme ];
89 }
90 }
91
92 if ( null !== $args['errors'] ) {
93 foreach ( $themes as $theme => $wp_theme ) {
94 if ( (bool) $wp_theme->errors() !== $args['errors'] ) {
95 unset( $themes[ $theme ] );
96 }
97 }
98 }
99
100 return $themes;
101}
102
103/**
104 * Gets a WP_Theme object for a theme.
105 *
106 * @since 3.4.0
107 *
108 * @global string[] $wp_theme_directories
109 *
110 * @param string $stylesheet Optional. Directory name for the theme. Defaults to active theme.
111 * @param string $theme_root Optional. Absolute path of the theme root to look in.
112 * If not specified, get_raw_theme_root() is used to calculate
113 * the theme root for the $stylesheet provided (or active theme).
114 * @return WP_Theme Theme object. Be sure to check the object's exists() method
115 * if you need to confirm the theme's existence.
116 */
117function wp_get_theme( $stylesheet = '', $theme_root = '' ) {
118 global $wp_theme_directories;
119
120 if ( empty( $stylesheet ) ) {
121 $stylesheet = get_stylesheet();
122 }
123
124 if ( empty( $theme_root ) ) {
125 $theme_root = get_raw_theme_root( $stylesheet );
126 if ( false === $theme_root ) {
127 $theme_root = WP_CONTENT_DIR . '/themes';
128 } elseif ( ! in_array( $theme_root, (array) $wp_theme_directories, true ) ) {
129 $theme_root = WP_CONTENT_DIR . $theme_root;
130 }
131 }
132
133 return new WP_Theme( $stylesheet, $theme_root );
134}
135
136/**
137 * Clears the cache held by get_theme_roots() and WP_Theme.
138 *
139 * @since 3.5.0
140 * @param bool $clear_update_cache Whether to clear the theme updates cache.
141 */
142function wp_clean_themes_cache( $clear_update_cache = true ) {
143 if ( $clear_update_cache ) {
144 delete_site_transient( 'update_themes' );
145 }
146 search_theme_directories( true );
147 foreach ( wp_get_themes( array( 'errors' => null ) ) as $theme ) {
148 $theme->cache_delete();
149 }
150}
151
152/**
153 * Whether a child theme is in use.
154 *
155 * @since 3.0.0
156 * @since 6.5.0 Makes use of global template variables.
157 *
158 * @global string $wp_stylesheet_path Path to current theme's stylesheet directory.
159 * @global string $wp_template_path Path to current theme's template directory.
160 *
161 * @return bool True if a child theme is in use, false otherwise.
162 */
163function is_child_theme() {
164 global $wp_stylesheet_path, $wp_template_path;
165
166 return $wp_stylesheet_path !== $wp_template_path;
167}
168
169/**
170 * Retrieves name of the current stylesheet.
171 *
172 * The theme name that is currently set as the front end theme.
173 *
174 * For all intents and purposes, the template name and the stylesheet name
175 * are going to be the same for most cases.
176 *
177 * @since 1.5.0
178 *
179 * @return string Stylesheet name.
180 */
181function get_stylesheet() {
182 /**
183 * Filters the name of current stylesheet.
184 *
185 * @since 1.5.0
186 *
187 * @param string $stylesheet Name of the current stylesheet.
188 */
189 return apply_filters( 'stylesheet', get_option( 'stylesheet' ) );
190}
191
192/**
193 * Retrieves stylesheet directory path for the active theme.
194 *
195 * @since 1.5.0
196 * @since 6.4.0 Memoizes filter execution so that it only runs once for the current theme.
197 * @since 6.4.2 Memoization removed.
198 *
199 * @return string Path to active theme's stylesheet directory.
200 */
201function get_stylesheet_directory() {
202 $stylesheet = get_stylesheet();
203 $theme_root = get_theme_root( $stylesheet );
204 $stylesheet_dir = "$theme_root/$stylesheet";
205
206 /**
207 * Filters the stylesheet directory path for the active theme.
208 *
209 * @since 1.5.0
210 *
211 * @param string $stylesheet_dir Absolute path to the active theme.
212 * @param string $stylesheet Directory name of the active theme.
213 * @param string $theme_root Absolute path to themes directory.
214 */
215 return apply_filters( 'stylesheet_directory', $stylesheet_dir, $stylesheet, $theme_root );
216}
217
218/**
219 * Retrieves stylesheet directory URI for the active theme.
220 *
221 * @since 1.5.0
222 *
223 * @return string URI to active theme's stylesheet directory.
224 */
225function get_stylesheet_directory_uri() {
226 $stylesheet = str_replace( '%2F', '/', rawurlencode( get_stylesheet() ) );
227 $theme_root_uri = get_theme_root_uri( $stylesheet );
228 $stylesheet_dir_uri = "$theme_root_uri/$stylesheet";
229
230 /**
231 * Filters the stylesheet directory URI.
232 *
233 * @since 1.5.0
234 *
235 * @param string $stylesheet_dir_uri Stylesheet directory URI.
236 * @param string $stylesheet Name of the activated theme's directory.
237 * @param string $theme_root_uri Themes root URI.
238 */
239 return apply_filters( 'stylesheet_directory_uri', $stylesheet_dir_uri, $stylesheet, $theme_root_uri );
240}
241
242/**
243 * Retrieves stylesheet URI for the active theme.
244 *
245 * The stylesheet file name is 'style.css' which is appended to the stylesheet directory URI path.
246 * See get_stylesheet_directory_uri().
247 *
248 * @since 1.5.0
249 *
250 * @return string URI to active theme's stylesheet.
251 */
252function get_stylesheet_uri() {
253 $stylesheet_dir_uri = get_stylesheet_directory_uri();
254 $stylesheet_uri = $stylesheet_dir_uri . '/style.css';
255 /**
256 * Filters the URI of the active theme stylesheet.
257 *
258 * @since 1.5.0
259 *
260 * @param string $stylesheet_uri Stylesheet URI for the active theme/child theme.
261 * @param string $stylesheet_dir_uri Stylesheet directory URI for the active theme/child theme.
262 */
263 return apply_filters( 'stylesheet_uri', $stylesheet_uri, $stylesheet_dir_uri );
264}
265
266/**
267 * Retrieves the localized stylesheet URI.
268 *
269 * The stylesheet directory for the localized stylesheet files are located, by
270 * default, in the base theme directory. The name of the locale file will be the
271 * locale followed by '.css'. If that does not exist, then the text direction
272 * stylesheet will be checked for existence, for example 'ltr.css'.
273 *
274 * The theme may change the location of the stylesheet directory by either using
275 * the {@see 'stylesheet_directory_uri'} or {@see 'locale_stylesheet_uri'} filters.
276 *
277 * If you want to change the location of the stylesheet files for the entire
278 * WordPress workflow, then change the former. If you just have the locale in a
279 * separate folder, then change the latter.
280 *
281 * @since 2.1.0
282 *
283 * @global WP_Locale $wp_locale WordPress date and time locale object.
284 *
285 * @return string URI to active theme's localized stylesheet.
286 */
287function get_locale_stylesheet_uri() {
288 global $wp_locale;
289 $stylesheet_dir_uri = get_stylesheet_directory_uri();
290 $dir = get_stylesheet_directory();
291 $locale = get_locale();
292 if ( file_exists( "$dir/$locale.css" ) ) {
293 $stylesheet_uri = "$stylesheet_dir_uri/$locale.css";
294 } elseif ( ! empty( $wp_locale->text_direction ) && file_exists( "$dir/{$wp_locale->text_direction}.css" ) ) {
295 $stylesheet_uri = "$stylesheet_dir_uri/{$wp_locale->text_direction}.css";
296 } else {
297 $stylesheet_uri = '';
298 }
299 /**
300 * Filters the localized stylesheet URI.
301 *
302 * @since 2.1.0
303 *
304 * @param string $stylesheet_uri Localized stylesheet URI.
305 * @param string $stylesheet_dir_uri Stylesheet directory URI.
306 */
307 return apply_filters( 'locale_stylesheet_uri', $stylesheet_uri, $stylesheet_dir_uri );
308}
309
310/**
311 * Retrieves name of the active theme.
312 *
313 * @since 1.5.0
314 *
315 * @return string Template name.
316 */
317function get_template() {
318 /**
319 * Filters the name of the active theme.
320 *
321 * @since 1.5.0
322 *
323 * @param string $template active theme's directory name.
324 */
325 return apply_filters( 'template', get_option( 'template' ) );
326}
327
328/**
329 * Retrieves template directory path for the active theme.
330 *
331 * @since 1.5.0
332 * @since 6.4.0 Memoizes filter execution so that it only runs once for the current theme.
333 * @since 6.4.1 Memoization removed.
334 *
335 * @return string Path to active theme's template directory.
336 */
337function get_template_directory() {
338 $template = get_template();
339 $theme_root = get_theme_root( $template );
340 $template_dir = "$theme_root/$template";
341
342 /**
343 * Filters the active theme directory path.
344 *
345 * @since 1.5.0
346 *
347 * @param string $template_dir The path of the active theme directory.
348 * @param string $template Directory name of the active theme.
349 * @param string $theme_root Absolute path to the themes directory.
350 */
351 return apply_filters( 'template_directory', $template_dir, $template, $theme_root );
352}
353
354/**
355 * Retrieves template directory URI for the active theme.
356 *
357 * @since 1.5.0
358 *
359 * @return string URI to active theme's template directory.
360 */
361function get_template_directory_uri() {
362 $template = str_replace( '%2F', '/', rawurlencode( get_template() ) );
363 $theme_root_uri = get_theme_root_uri( $template );
364 $template_dir_uri = "$theme_root_uri/$template";
365
366 /**
367 * Filters the active theme directory URI.
368 *
369 * @since 1.5.0
370 *
371 * @param string $template_dir_uri The URI of the active theme directory.
372 * @param string $template Directory name of the active theme.
373 * @param string $theme_root_uri The themes root URI.
374 */
375 return apply_filters( 'template_directory_uri', $template_dir_uri, $template, $theme_root_uri );
376}
377
378/**
379 * Retrieves theme roots.
380 *
381 * @since 2.9.0
382 *
383 * @global string[] $wp_theme_directories
384 *
385 * @return array|string An array of theme roots keyed by template/stylesheet
386 * or a single theme root if all themes have the same root.
387 */
388function get_theme_roots() {
389 global $wp_theme_directories;
390
391 if ( ! is_array( $wp_theme_directories ) || count( $wp_theme_directories ) <= 1 ) {
392 return '/themes';
393 }
394
395 $theme_roots = get_site_transient( 'theme_roots' );
396 if ( false === $theme_roots ) {
397 search_theme_directories( true ); // Regenerate the transient.
398 $theme_roots = get_site_transient( 'theme_roots' );
399 }
400 return $theme_roots;
401}
402
403/**
404 * Registers a directory that contains themes.
405 *
406 * @since 2.9.0
407 *
408 * @global string[] $wp_theme_directories
409 *
410 * @param string $directory Either the full filesystem path to a theme folder
411 * or a folder within WP_CONTENT_DIR.
412 * @return bool True if successfully registered a directory that contains themes,
413 * false if the directory does not exist.
414 */
415function register_theme_directory( $directory ) {
416 global $wp_theme_directories;
417
418 if ( ! file_exists( $directory ) ) {
419 // Try prepending as the theme directory could be relative to the content directory.
420 $directory = WP_CONTENT_DIR . '/' . $directory;
421 // If this directory does not exist, return and do not register.
422 if ( ! file_exists( $directory ) ) {
423 return false;
424 }
425 }
426
427 if ( ! is_array( $wp_theme_directories ) ) {
428 $wp_theme_directories = array();
429 }
430
431 $untrailed = untrailingslashit( $directory );
432 if ( ! empty( $untrailed ) && ! in_array( $untrailed, $wp_theme_directories, true ) ) {
433 $wp_theme_directories[] = $untrailed;
434 }
435
436 return true;
437}
438
439/**
440 * Searches all registered theme directories for complete and valid themes.
441 *
442 * @since 2.9.0
443 *
444 * @global string[] $wp_theme_directories
445 *
446 * @param bool $force Optional. Whether to force a new directory scan. Default false.
447 * @return array|false Valid themes found on success, false on failure.
448 */
449function search_theme_directories( $force = false ) {
450 global $wp_theme_directories;
451 static $found_themes = null;
452
453 if ( empty( $wp_theme_directories ) ) {
454 return false;
455 }
456
457 if ( ! $force && isset( $found_themes ) ) {
458 return $found_themes;
459 }
460
461 $found_themes = array();
462
463 $wp_theme_directories = (array) $wp_theme_directories;
464 $relative_theme_roots = array();
465
466 /*
467 * Set up maybe-relative, maybe-absolute array of theme directories.
468 * We always want to return absolute, but we need to cache relative
469 * to use in get_theme_root().
470 */
471 foreach ( $wp_theme_directories as $theme_root ) {
472 if ( str_starts_with( $theme_root, WP_CONTENT_DIR ) ) {
473 $relative_theme_roots[ str_replace( WP_CONTENT_DIR, '', $theme_root ) ] = $theme_root;
474 } else {
475 $relative_theme_roots[ $theme_root ] = $theme_root;
476 }
477 }
478
479 /**
480 * Filters whether to get the cache of the registered theme directories.
481 *
482 * @since 3.4.0
483 *
484 * @param bool $cache_expiration Whether to get the cache of the theme directories. Default false.
485 * @param string $context The class or function name calling the filter.
486 */
487 $cache_expiration = apply_filters( 'wp_cache_themes_persistently', false, 'search_theme_directories' );
488
489 if ( $cache_expiration ) {
490 $cached_roots = get_site_transient( 'theme_roots' );
491 if ( is_array( $cached_roots ) ) {
492 foreach ( $cached_roots as $theme_dir => $theme_root ) {
493 // A cached theme root is no longer around, so skip it.
494 if ( ! isset( $relative_theme_roots[ $theme_root ] ) ) {
495 continue;
496 }
497 $found_themes[ $theme_dir ] = array(
498 'theme_file' => $theme_dir . '/style.css',
499 'theme_root' => $relative_theme_roots[ $theme_root ], // Convert relative to absolute.
500 );
501 }
502 return $found_themes;
503 }
504 if ( ! is_int( $cache_expiration ) ) {
505 $cache_expiration = 30 * MINUTE_IN_SECONDS;
506 }
507 } else {
508 $cache_expiration = 30 * MINUTE_IN_SECONDS;
509 }
510
511 /* Loop the registered theme directories and extract all themes */
512 foreach ( $wp_theme_directories as $theme_root ) {
513
514 // Start with directories in the root of the active theme directory.
515 $dirs = @ scandir( $theme_root );
516 if ( ! $dirs ) {
517 wp_trigger_error( __FUNCTION__, "$theme_root is not readable" );
518 continue;
519 }
520 foreach ( $dirs as $dir ) {
521 if ( ! is_dir( $theme_root . '/' . $dir ) || '.' === $dir[0] || 'CVS' === $dir ) {
522 continue;
523 }
524 if ( file_exists( $theme_root . '/' . $dir . '/style.css' ) ) {
525 /*
526 * wp-content/themes/a-single-theme
527 * wp-content/themes is $theme_root, a-single-theme is $dir.
528 */
529 $found_themes[ $dir ] = array(
530 'theme_file' => $dir . '/style.css',
531 'theme_root' => $theme_root,
532 );
533 } else {
534 $found_theme = false;
535 /*
536 * wp-content/themes/a-folder-of-themes/*
537 * wp-content/themes is $theme_root, a-folder-of-themes is $dir, then themes are $sub_dirs.
538 */
539 $sub_dirs = @ scandir( $theme_root . '/' . $dir );
540 if ( ! $sub_dirs ) {
541 wp_trigger_error( __FUNCTION__, "$theme_root/$dir is not readable" );
542 continue;
543 }
544 foreach ( $sub_dirs as $sub_dir ) {
545 if ( ! is_dir( $theme_root . '/' . $dir . '/' . $sub_dir ) || '.' === $dir[0] || 'CVS' === $dir ) {
546 continue;
547 }
548 if ( ! file_exists( $theme_root . '/' . $dir . '/' . $sub_dir . '/style.css' ) ) {
549 continue;
550 }
551 $found_themes[ $dir . '/' . $sub_dir ] = array(
552 'theme_file' => $dir . '/' . $sub_dir . '/style.css',
553 'theme_root' => $theme_root,
554 );
555 $found_theme = true;
556 }
557 /*
558 * Never mind the above, it's just a theme missing a style.css.
559 * Return it; WP_Theme will catch the error.
560 */
561 if ( ! $found_theme ) {
562 $found_themes[ $dir ] = array(
563 'theme_file' => $dir . '/style.css',
564 'theme_root' => $theme_root,
565 );
566 }
567 }
568 }
569 }
570
571 asort( $found_themes );
572
573 $theme_roots = array();
574 $relative_theme_roots = array_flip( $relative_theme_roots );
575
576 foreach ( $found_themes as $theme_dir => $theme_data ) {
577 $theme_roots[ $theme_dir ] = $relative_theme_roots[ $theme_data['theme_root'] ]; // Convert absolute to relative.
578 }
579
580 if ( get_site_transient( 'theme_roots' ) !== $theme_roots ) {
581 set_site_transient( 'theme_roots', $theme_roots, $cache_expiration );
582 }
583
584 return $found_themes;
585}
586
587/**
588 * Retrieves path to themes directory.
589 *
590 * Does not have trailing slash.
591 *
592 * @since 1.5.0
593 *
594 * @global string[] $wp_theme_directories
595 *
596 * @param string $stylesheet_or_template Optional. The stylesheet or template name of the theme.
597 * Default is to leverage the main theme root.
598 * @return string Themes directory path.
599 */
600function get_theme_root( $stylesheet_or_template = '' ) {
601 global $wp_theme_directories;
602
603 $theme_root = '';
604
605 if ( $stylesheet_or_template ) {
606 $theme_root = get_raw_theme_root( $stylesheet_or_template );
607 if ( $theme_root ) {
608 /*
609 * Always prepend WP_CONTENT_DIR unless the root currently registered as a theme directory.
610 * This gives relative theme roots the benefit of the doubt when things go haywire.
611 */
612 if ( ! in_array( $theme_root, (array) $wp_theme_directories, true ) ) {
613 $theme_root = WP_CONTENT_DIR . $theme_root;
614 }
615 }
616 }
617
618 if ( ! $theme_root ) {
619 $theme_root = WP_CONTENT_DIR . '/themes';
620 }
621
622 /**
623 * Filters the absolute path to the themes directory.
624 *
625 * @since 1.5.0
626 *
627 * @param string $theme_root Absolute path to themes directory.
628 */
629 return apply_filters( 'theme_root', $theme_root );
630}
631
632/**
633 * Retrieves URI for themes directory.
634 *
635 * Does not have trailing slash.
636 *
637 * @since 1.5.0
638 *
639 * @global string[] $wp_theme_directories
640 *
641 * @param string $stylesheet_or_template Optional. The stylesheet or template name of the theme.
642 * Default is to leverage the main theme root.
643 * @param string $theme_root Optional. The theme root for which calculations will be based,
644 * preventing the need for a get_raw_theme_root() call. Default empty.
645 * @return string Themes directory URI.
646 */
647function get_theme_root_uri( $stylesheet_or_template = '', $theme_root = '' ) {
648 global $wp_theme_directories;
649
650 if ( $stylesheet_or_template && ! $theme_root ) {
651 $theme_root = get_raw_theme_root( $stylesheet_or_template );
652 }
653
654 if ( $stylesheet_or_template && $theme_root ) {
655 if ( in_array( $theme_root, (array) $wp_theme_directories, true ) ) {
656 // Absolute path. Make an educated guess. YMMV -- but note the filter below.
657 if ( str_starts_with( $theme_root, WP_CONTENT_DIR ) ) {
658 $theme_root_uri = content_url( str_replace( WP_CONTENT_DIR, '', $theme_root ) );
659 } elseif ( str_starts_with( $theme_root, ABSPATH ) ) {
660 $theme_root_uri = site_url( str_replace( ABSPATH, '', $theme_root ) );
661 } elseif ( str_starts_with( $theme_root, WP_PLUGIN_DIR ) || str_starts_with( $theme_root, WPMU_PLUGIN_DIR ) ) {
662 $theme_root_uri = plugins_url( basename( $theme_root ), $theme_root );
663 } else {
664 $theme_root_uri = $theme_root;
665 }
666 } else {
667 $theme_root_uri = content_url( $theme_root );
668 }
669 } else {
670 $theme_root_uri = content_url( 'themes' );
671 }
672
673 /**
674 * Filters the URI for themes directory.
675 *
676 * @since 1.5.0
677 *
678 * @param string $theme_root_uri The URI for themes directory.
679 * @param string $siteurl WordPress web address which is set in General Options.
680 * @param string $stylesheet_or_template The stylesheet or template name of the theme.
681 */
682 return apply_filters( 'theme_root_uri', $theme_root_uri, get_option( 'siteurl' ), $stylesheet_or_template );
683}
684
685/**
686 * Gets the raw theme root relative to the content directory with no filters applied.
687 *
688 * @since 3.1.0
689 *
690 * @global string[] $wp_theme_directories
691 *
692 * @param string $stylesheet_or_template The stylesheet or template name of the theme.
693 * @param bool $skip_cache Optional. Whether to skip the cache.
694 * Defaults to false, meaning the cache is used.
695 * @return string Theme root.
696 */
697function get_raw_theme_root( $stylesheet_or_template, $skip_cache = false ) {
698 global $wp_theme_directories;
699
700 if ( ! is_array( $wp_theme_directories ) || count( $wp_theme_directories ) <= 1 ) {
701 return '/themes';
702 }
703
704 $theme_root = false;
705
706 // If requesting the root for the active theme, consult options to avoid calling get_theme_roots().
707 if ( ! $skip_cache ) {
708 if ( get_option( 'stylesheet' ) === $stylesheet_or_template ) {
709 $theme_root = get_option( 'stylesheet_root' );
710 } elseif ( get_option( 'template' ) === $stylesheet_or_template ) {
711 $theme_root = get_option( 'template_root' );
712 }
713 }
714
715 if ( empty( $theme_root ) ) {
716 $theme_roots = get_theme_roots();
717 if ( ! empty( $theme_roots[ $stylesheet_or_template ] ) ) {
718 $theme_root = $theme_roots[ $stylesheet_or_template ];
719 }
720 }
721
722 return $theme_root;
723}
724
725/**
726 * Displays localized stylesheet link element.
727 *
728 * @since 2.1.0
729 */
730function locale_stylesheet() {
731 $stylesheet = get_locale_stylesheet_uri();
732 if ( empty( $stylesheet ) ) {
733 return;
734 }
735
736 $type_attr = current_theme_supports( 'html5', 'style' ) ? '' : ' type="text/css"';
737
738 printf(
739 '<link rel="stylesheet" href="%s"%s media="screen" />',
740 $stylesheet,
741 $type_attr
742 );
743}
744
745/**
746 * Switches the theme.
747 *
748 * Accepts one argument: $stylesheet of the theme. It also accepts an additional function signature
749 * of two arguments: $template then $stylesheet. This is for backward compatibility.
750 *
751 * @since 2.5.0
752 *
753 * @global string[] $wp_theme_directories
754 * @global WP_Customize_Manager $wp_customize
755 * @global array $sidebars_widgets
756 * @global array $wp_registered_sidebars
757 *
758 * @param string $stylesheet Stylesheet name.
759 */
760function switch_theme( $stylesheet ) {
761 global $wp_theme_directories, $wp_customize, $sidebars_widgets, $wp_registered_sidebars;
762
763 $requirements = validate_theme_requirements( $stylesheet );
764 if ( is_wp_error( $requirements ) ) {
765 wp_die( $requirements );
766 }
767
768 $_sidebars_widgets = null;
769 if ( 'wp_ajax_customize_save' === current_action() ) {
770 $old_sidebars_widgets_data_setting = $wp_customize->get_setting( 'old_sidebars_widgets_data' );
771 if ( $old_sidebars_widgets_data_setting ) {
772 $_sidebars_widgets = $wp_customize->post_value( $old_sidebars_widgets_data_setting );
773 }
774 } elseif ( is_array( $sidebars_widgets ) ) {
775 $_sidebars_widgets = $sidebars_widgets;
776 }
777
778 if ( is_array( $_sidebars_widgets ) ) {
779 set_theme_mod(
780 'sidebars_widgets',
781 array(
782 'time' => time(),
783 'data' => $_sidebars_widgets,
784 )
785 );
786 }
787
788 $nav_menu_locations = get_theme_mod( 'nav_menu_locations' );
789 update_option( 'theme_switch_menu_locations', $nav_menu_locations, true );
790
791 if ( func_num_args() > 1 ) {
792 $stylesheet = func_get_arg( 1 );
793 }
794
795 $old_theme = wp_get_theme();
796 $new_theme = wp_get_theme( $stylesheet );
797 $template = $new_theme->get_template();
798
799 if ( wp_is_recovery_mode() ) {
800 $paused_themes = wp_paused_themes();
801 $paused_themes->delete( $old_theme->get_stylesheet() );
802 $paused_themes->delete( $old_theme->get_template() );
803 }
804
805 update_option( 'template', $template );
806 update_option( 'stylesheet', $stylesheet );
807
808 if ( count( $wp_theme_directories ) > 1 ) {
809 update_option( 'template_root', get_raw_theme_root( $template, true ) );
810 update_option( 'stylesheet_root', get_raw_theme_root( $stylesheet, true ) );
811 } else {
812 delete_option( 'template_root' );
813 delete_option( 'stylesheet_root' );
814 }
815
816 $new_name = $new_theme->get( 'Name' );
817
818 update_option( 'current_theme', $new_name );
819
820 // Migrate from the old mods_{name} option to theme_mods_{slug}.
821 if ( is_admin() && false === get_option( 'theme_mods_' . $stylesheet ) ) {
822 $default_theme_mods = (array) get_option( 'mods_' . $new_name );
823 if ( ! empty( $nav_menu_locations ) && empty( $default_theme_mods['nav_menu_locations'] ) ) {
824 $default_theme_mods['nav_menu_locations'] = $nav_menu_locations;
825 }
826 add_option( "theme_mods_$stylesheet", $default_theme_mods );
827 } else {
828 /*
829 * Since retrieve_widgets() is called when initializing a theme in the Customizer,
830 * we need to remove the theme mods to avoid overwriting changes made via
831 * the Customizer when accessing wp-admin/widgets.php.
832 */
833 if ( 'wp_ajax_customize_save' === current_action() ) {
834 remove_theme_mod( 'sidebars_widgets' );
835 }
836 }
837
838 // Stores classic sidebars for later use by block themes.
839 if ( $new_theme->is_block_theme() ) {
840 set_theme_mod( 'wp_classic_sidebars', $wp_registered_sidebars );
841 }
842
843 update_option( 'theme_switched', $old_theme->get_stylesheet() );
844
845 /*
846 * Reset template globals when switching themes outside of a switched blog
847 * context to ensure templates will be loaded from the new theme.
848 */
849 if ( ! is_multisite() || ! ms_is_switched() ) {
850 wp_set_template_globals();
851 }
852
853 // Clear pattern caches.
854 if ( ! is_multisite() ) {
855 $new_theme->delete_pattern_cache();
856 $old_theme->delete_pattern_cache();
857 }
858
859 // Set autoload=no for the old theme, autoload=yes for the switched theme.
860 $theme_mods_options = array(
861 'theme_mods_' . $stylesheet => 'yes',
862 'theme_mods_' . $old_theme->get_stylesheet() => 'no',
863 );
864 wp_set_option_autoload_values( $theme_mods_options );
865
866 /**
867 * Fires after the theme is switched.
868 *
869 * See {@see 'after_switch_theme'}.
870 *
871 * @since 1.5.0
872 * @since 4.5.0 Introduced the `$old_theme` parameter.
873 *
874 * @param string $new_name Name of the new theme.
875 * @param WP_Theme $new_theme WP_Theme instance of the new theme.
876 * @param WP_Theme $old_theme WP_Theme instance of the old theme.
877 */
878 do_action( 'switch_theme', $new_name, $new_theme, $old_theme );
879}
880
881/**
882 * Checks that the active theme has the required files.
883 *
884 * Standalone themes need to have a `templates/index.html` or `index.php` template file.
885 * Child themes need to have a `Template` header in the `style.css` stylesheet.
886 *
887 * Does not initially check the default theme, which is the fallback and should always exist.
888 * But if it doesn't exist, it'll fall back to the latest core default theme that does exist.
889 * Will switch theme to the fallback theme if active theme does not validate.
890 *
891 * You can use the {@see 'validate_current_theme'} filter to return false to disable
892 * this functionality.
893 *
894 * @since 1.5.0
895 * @since 6.0.0 Removed the requirement for block themes to have an `index.php` template.
896 *
897 * @see WP_DEFAULT_THEME
898 *
899 * @return bool
900 */
901function validate_current_theme() {
902 /**
903 * Filters whether to validate the active theme.
904 *
905 * @since 2.7.0
906 *
907 * @param bool $validate Whether to validate the active theme. Default true.
908 */
909 if ( wp_installing() || ! apply_filters( 'validate_current_theme', true ) ) {
910 return true;
911 }
912
913 if (
914 ! file_exists( get_template_directory() . '/templates/index.html' )
915 && ! file_exists( get_template_directory() . '/block-templates/index.html' ) // Deprecated path support since 5.9.0.
916 && ! file_exists( get_template_directory() . '/index.php' )
917 ) {
918 // Invalid.
919 } elseif ( ! file_exists( get_template_directory() . '/style.css' ) ) {
920 // Invalid.
921 } elseif ( is_child_theme() && ! file_exists( get_stylesheet_directory() . '/style.css' ) ) {
922 // Invalid.
923 } else {
924 // Valid.
925 return true;
926 }
927
928 $default = wp_get_theme( WP_DEFAULT_THEME );
929 if ( $default->exists() ) {
930 switch_theme( WP_DEFAULT_THEME );
931 return false;
932 }
933
934 /**
935 * If we're in an invalid state but WP_DEFAULT_THEME doesn't exist,
936 * switch to the latest core default theme that's installed.
937 *
938 * If it turns out that this latest core default theme is our current
939 * theme, then there's nothing we can do about that, so we have to bail,
940 * rather than going into an infinite loop. (This is why there are
941 * checks against WP_DEFAULT_THEME above, also.) We also can't do anything
942 * if it turns out there is no default theme installed. (That's `false`.)
943 */
944 $default = WP_Theme::get_core_default_theme();
945 if ( false === $default || get_stylesheet() === $default->get_stylesheet() ) {
946 return true;
947 }
948
949 switch_theme( $default->get_stylesheet() );
950 return false;
951}
952
953/**
954 * Validates the theme requirements for WordPress version and PHP version.
955 *
956 * Uses the information from `Requires at least` and `Requires PHP` headers
957 * defined in the theme's `style.css` file.
958 *
959 * @since 5.5.0
960 * @since 5.8.0 Removed support for using `readme.txt` as a fallback.
961 *
962 * @param string $stylesheet Directory name for the theme.
963 * @return true|WP_Error True if requirements are met, WP_Error on failure.
964 */
965function validate_theme_requirements( $stylesheet ) {
966 $theme = wp_get_theme( $stylesheet );
967
968 $requirements = array(
969 'requires' => ! empty( $theme->get( 'RequiresWP' ) ) ? $theme->get( 'RequiresWP' ) : '',
970 'requires_php' => ! empty( $theme->get( 'RequiresPHP' ) ) ? $theme->get( 'RequiresPHP' ) : '',
971 );
972
973 $compatible_wp = is_wp_version_compatible( $requirements['requires'] );
974 $compatible_php = is_php_version_compatible( $requirements['requires_php'] );
975
976 if ( ! $compatible_wp && ! $compatible_php ) {
977 return new WP_Error(
978 'theme_wp_php_incompatible',
979 sprintf(
980 /* translators: %s: Theme name. */
981 _x( '<strong>Error:</strong> Current WordPress and PHP versions do not meet minimum requirements for %s.', 'theme' ),
982 $theme->display( 'Name' )
983 )
984 );
985 } elseif ( ! $compatible_php ) {
986 return new WP_Error(
987 'theme_php_incompatible',
988 sprintf(
989 /* translators: %s: Theme name. */
990 _x( '<strong>Error:</strong> Current PHP version does not meet minimum requirements for %s.', 'theme' ),
991 $theme->display( 'Name' )
992 )
993 );
994 } elseif ( ! $compatible_wp ) {
995 return new WP_Error(
996 'theme_wp_incompatible',
997 sprintf(
998 /* translators: %s: Theme name. */
999 _x( '<strong>Error:</strong> Current WordPress version does not meet minimum requirements for %s.', 'theme' ),
1000 $theme->display( 'Name' )
1001 )
1002 );
1003 }
1004
1005 /**
1006 * Filters the theme requirement validation response.
1007 *
1008 * If a theme fails due to a Core-provided validation (incompatible WP, PHP versions), this
1009 * filter will not fire. A WP_Error response will already be returned.
1010 *
1011 * This filter is intended to add additional validation steps by site administrators.
1012 *
1013 * @since 6.9.0
1014 *
1015 * @param bool|WP_Error $met_requirements True if the theme meets requirements, WP_Error if not.
1016 * @param string $stylesheet Directory name for the theme.
1017 */
1018 return apply_filters( 'validate_theme_requirements', true, $stylesheet );
1019}
1020
1021/**
1022 * Retrieves all theme modifications.
1023 *
1024 * @since 3.1.0
1025 * @since 5.9.0 The return value is always an array.
1026 *
1027 * @return array Theme modifications.
1028 */
1029function get_theme_mods() {
1030 $theme_slug = get_option( 'stylesheet' );
1031 $mods = get_option( "theme_mods_$theme_slug" );
1032
1033 if ( false === $mods ) {
1034 $theme_name = get_option( 'current_theme' );
1035 if ( false === $theme_name ) {
1036 $theme_name = wp_get_theme()->get( 'Name' );
1037 }
1038
1039 $mods = get_option( "mods_$theme_name" ); // Deprecated location.
1040 if ( is_admin() && false !== $mods ) {
1041 update_option( "theme_mods_$theme_slug", $mods );
1042 delete_option( "mods_$theme_name" );
1043 }
1044 }
1045
1046 if ( ! is_array( $mods ) ) {
1047 $mods = array();
1048 }
1049
1050 return $mods;
1051}
1052
1053/**
1054 * Retrieves theme modification value for the active theme.
1055 *
1056 * If the modification name does not exist and `$default_value` is a string, then the
1057 * default will be passed through the {@link https://www.php.net/sprintf sprintf()}
1058 * PHP function with the template directory URI as the first value and the
1059 * stylesheet directory URI as the second value.
1060 *
1061 * @since 2.1.0
1062 *
1063 * @param string $name Theme modification name.
1064 * @param mixed $default_value Optional. Theme modification default value. Default false.
1065 * @return mixed Theme modification value.
1066 */
1067function get_theme_mod( $name, $default_value = false ) {
1068 $mods = get_theme_mods();
1069
1070 if ( isset( $mods[ $name ] ) ) {
1071 /**
1072 * Filters the theme modification, or 'theme_mod', value.
1073 *
1074 * The dynamic portion of the hook name, `$name`, refers to the key name
1075 * of the modification array. For example, 'header_textcolor', 'header_image',
1076 * and so on depending on the theme options.
1077 *
1078 * @since 2.2.0
1079 *
1080 * @param mixed $current_mod The value of the active theme modification.
1081 */
1082 return apply_filters( "theme_mod_{$name}", $mods[ $name ] );
1083 }
1084
1085 if ( is_string( $default_value ) ) {
1086 // Only run the replacement if an sprintf() string format pattern was found.
1087 if ( preg_match( '#(?<!%)%(?:\d+\$?)?s#', $default_value ) ) {
1088 // Remove a single trailing percent sign.
1089 $default_value = preg_replace( '#(?<!%)%$#', '', $default_value );
1090 $default_value = sprintf( $default_value, get_template_directory_uri(), get_stylesheet_directory_uri() );
1091 }
1092 }
1093
1094 /** This filter is documented in wp-includes/theme.php */
1095 return apply_filters( "theme_mod_{$name}", $default_value );
1096}
1097
1098/**
1099 * Updates theme modification value for the active theme.
1100 *
1101 * @since 2.1.0
1102 * @since 5.6.0 A return value was added.
1103 *
1104 * @param string $name Theme modification name.
1105 * @param mixed $value Theme modification value.
1106 * @return bool True if the value was updated, false otherwise.
1107 */
1108function set_theme_mod( $name, $value ) {
1109 $mods = get_theme_mods();
1110 $old_value = isset( $mods[ $name ] ) ? $mods[ $name ] : false;
1111
1112 /**
1113 * Filters the theme modification, or 'theme_mod', value on save.
1114 *
1115 * The dynamic portion of the hook name, `$name`, refers to the key name
1116 * of the modification array. For example, 'header_textcolor', 'header_image',
1117 * and so on depending on the theme options.
1118 *
1119 * @since 3.9.0
1120 *
1121 * @param mixed $value The new value of the theme modification.
1122 * @param mixed $old_value The current value of the theme modification.
1123 */
1124 $mods[ $name ] = apply_filters( "pre_set_theme_mod_{$name}", $value, $old_value );
1125
1126 $theme = get_option( 'stylesheet' );
1127
1128 return update_option( "theme_mods_$theme", $mods );
1129}
1130
1131/**
1132 * Removes theme modification name from active theme list.
1133 *
1134 * If removing the name also removes all elements, then the entire option
1135 * will be removed.
1136 *
1137 * @since 2.1.0
1138 *
1139 * @param string $name Theme modification name.
1140 */
1141function remove_theme_mod( $name ) {
1142 $mods = get_theme_mods();
1143
1144 if ( ! isset( $mods[ $name ] ) ) {
1145 return;
1146 }
1147
1148 unset( $mods[ $name ] );
1149
1150 if ( empty( $mods ) ) {
1151 remove_theme_mods();
1152 return;
1153 }
1154
1155 $theme = get_option( 'stylesheet' );
1156
1157 update_option( "theme_mods_$theme", $mods );
1158}
1159
1160/**
1161 * Removes theme modifications option for the active theme.
1162 *
1163 * @since 2.1.0
1164 */
1165function remove_theme_mods() {
1166 delete_option( 'theme_mods_' . get_option( 'stylesheet' ) );
1167
1168 // Old style.
1169 $theme_name = get_option( 'current_theme' );
1170 if ( false === $theme_name ) {
1171 $theme_name = wp_get_theme()->get( 'Name' );
1172 }
1173
1174 delete_option( 'mods_' . $theme_name );
1175}
1176
1177/**
1178 * Retrieves the custom header text color in 3- or 6-digit hexadecimal form.
1179 *
1180 * @since 2.1.0
1181 *
1182 * @return string Header text color in 3- or 6-digit hexadecimal form (minus the hash symbol).
1183 */
1184function get_header_textcolor() {
1185 return get_theme_mod( 'header_textcolor', get_theme_support( 'custom-header', 'default-text-color' ) );
1186}
1187
1188/**
1189 * Displays the custom header text color in 3- or 6-digit hexadecimal form (minus the hash symbol).
1190 *
1191 * @since 2.1.0
1192 */
1193function header_textcolor() {
1194 echo get_header_textcolor();
1195}
1196
1197/**
1198 * Whether to display the header text.
1199 *
1200 * @since 3.4.0
1201 *
1202 * @return bool
1203 */
1204function display_header_text() {
1205 if ( ! current_theme_supports( 'custom-header', 'header-text' ) ) {
1206 return false;
1207 }
1208
1209 $text_color = get_theme_mod( 'header_textcolor', get_theme_support( 'custom-header', 'default-text-color' ) );
1210 return 'blank' !== $text_color;
1211}
1212
1213/**
1214 * Checks whether a header image is set or not.
1215 *
1216 * @since 4.2.0
1217 *
1218 * @see get_header_image()
1219 *
1220 * @return bool Whether a header image is set or not.
1221 */
1222function has_header_image() {
1223 return (bool) get_header_image();
1224}
1225
1226/**
1227 * Retrieves header image for custom header.
1228 *
1229 * @since 2.1.0
1230 *
1231 * @return string|false
1232 */
1233function get_header_image() {
1234 $url = get_theme_mod( 'header_image', get_theme_support( 'custom-header', 'default-image' ) );
1235
1236 if ( 'remove-header' === $url ) {
1237 return false;
1238 }
1239
1240 if ( is_random_header_image() ) {
1241 $url = get_random_header_image();
1242 }
1243
1244 /**
1245 * Filters the header image URL.
1246 *
1247 * @since 6.1.0
1248 *
1249 * @param string $url Header image URL.
1250 */
1251 $url = apply_filters( 'get_header_image', $url );
1252
1253 if ( ! is_string( $url ) ) {
1254 return false;
1255 }
1256
1257 $url = trim( $url );
1258 return sanitize_url( set_url_scheme( $url ) );
1259}
1260
1261/**
1262 * Creates image tag markup for a custom header image.
1263 *
1264 * @since 4.4.0
1265 *
1266 * @param array $attr Optional. Additional attributes for the image tag. Can be used
1267 * to override the default attributes. Default empty.
1268 * @return string HTML image element markup or empty string on failure.
1269 */
1270function get_header_image_tag( $attr = array() ) {
1271 $header = get_custom_header();
1272 $header->url = get_header_image();
1273
1274 if ( ! $header->url ) {
1275 return '';
1276 }
1277
1278 $width = absint( $header->width );
1279 $height = absint( $header->height );
1280 $alt = '';
1281
1282 // Use alternative text assigned to the image, if available. Otherwise, leave it empty.
1283 if ( ! empty( $header->attachment_id ) ) {
1284 $image_alt = get_post_meta( $header->attachment_id, '_wp_attachment_image_alt', true );
1285
1286 if ( is_string( $image_alt ) ) {
1287 $alt = $image_alt;
1288 }
1289 }
1290
1291 $attr = wp_parse_args(
1292 $attr,
1293 array(
1294 'src' => $header->url,
1295 'width' => $width,
1296 'height' => $height,
1297 'alt' => $alt,
1298 )
1299 );
1300
1301 // Generate 'srcset' and 'sizes' if not already present.
1302 if ( empty( $attr['srcset'] ) && ! empty( $header->attachment_id ) ) {
1303 $image_meta = get_post_meta( $header->attachment_id, '_wp_attachment_metadata', true );
1304 $size_array = array( $width, $height );
1305
1306 if ( is_array( $image_meta ) ) {
1307 $srcset = wp_calculate_image_srcset( $size_array, $header->url, $image_meta, $header->attachment_id );
1308
1309 if ( ! empty( $attr['sizes'] ) ) {
1310 $sizes = $attr['sizes'];
1311 } else {
1312 $sizes = wp_calculate_image_sizes( $size_array, $header->url, $image_meta, $header->attachment_id );
1313 }
1314
1315 if ( $srcset && $sizes ) {
1316 $attr['srcset'] = $srcset;
1317 $attr['sizes'] = $sizes;
1318 }
1319 }
1320 }
1321
1322 $attr = array_merge(
1323 $attr,
1324 wp_get_loading_optimization_attributes( 'img', $attr, 'get_header_image_tag' )
1325 );
1326
1327 /*
1328 * If the default value of `lazy` for the `loading` attribute is overridden
1329 * to omit the attribute for this image, ensure it is not included.
1330 */
1331 if ( isset( $attr['loading'] ) && ! $attr['loading'] ) {
1332 unset( $attr['loading'] );
1333 }
1334
1335 // If the `fetchpriority` attribute is overridden and set to false or an empty string.
1336 if ( isset( $attr['fetchpriority'] ) && ! $attr['fetchpriority'] ) {
1337 unset( $attr['fetchpriority'] );
1338 }
1339
1340 // If the `decoding` attribute is overridden and set to false or an empty string.
1341 if ( isset( $attr['decoding'] ) && ! $attr['decoding'] ) {
1342 unset( $attr['decoding'] );
1343 }
1344
1345 /**
1346 * Filters the list of header image attributes.
1347 *
1348 * @since 5.9.0
1349 *
1350 * @param array $attr Array of the attributes for the image tag.
1351 * @param object $header The custom header object returned by 'get_custom_header()'.
1352 */
1353 $attr = apply_filters( 'get_header_image_tag_attributes', $attr, $header );
1354
1355 $attr = array_map( 'esc_attr', $attr );
1356 $html = '<img';
1357
1358 foreach ( $attr as $name => $value ) {
1359 $html .= ' ' . $name . '="' . $value . '"';
1360 }
1361
1362 $html .= ' />';
1363
1364 /**
1365 * Filters the markup of header images.
1366 *
1367 * @since 4.4.0
1368 *
1369 * @param string $html The HTML image tag markup being filtered.
1370 * @param object $header The custom header object returned by 'get_custom_header()'.
1371 * @param array $attr Array of the attributes for the image tag.
1372 */
1373 return apply_filters( 'get_header_image_tag', $html, $header, $attr );
1374}
1375
1376/**
1377 * Displays the image markup for a custom header image.
1378 *
1379 * @since 4.4.0
1380 *
1381 * @param array $attr Optional. Attributes for the image markup. Default empty.
1382 */
1383function the_header_image_tag( $attr = array() ) {
1384 echo get_header_image_tag( $attr );
1385}
1386
1387/**
1388 * Gets random header image data from registered images in theme.
1389 *
1390 * @since 3.4.0
1391 *
1392 * @access private
1393 *
1394 * @global array $_wp_default_headers
1395 *
1396 * @return object
1397 */
1398function _get_random_header_data() {
1399 global $_wp_default_headers;
1400 static $_wp_random_header = null;
1401
1402 if ( empty( $_wp_random_header ) ) {
1403 $header_image_mod = get_theme_mod( 'header_image', '' );
1404 $headers = array();
1405
1406 if ( 'random-uploaded-image' === $header_image_mod ) {
1407 $headers = get_uploaded_header_images();
1408 } elseif ( ! empty( $_wp_default_headers ) ) {
1409 if ( 'random-default-image' === $header_image_mod ) {
1410 $headers = $_wp_default_headers;
1411 } else {
1412 if ( current_theme_supports( 'custom-header', 'random-default' ) ) {
1413 $headers = $_wp_default_headers;
1414 }
1415 }
1416 }
1417
1418 if ( empty( $headers ) ) {
1419 return new stdClass();
1420 }
1421
1422 $_wp_random_header = (object) $headers[ array_rand( $headers ) ];
1423
1424 $_wp_random_header->url = sprintf(
1425 $_wp_random_header->url,
1426 get_template_directory_uri(),
1427 get_stylesheet_directory_uri()
1428 );
1429
1430 $_wp_random_header->thumbnail_url = sprintf(
1431 $_wp_random_header->thumbnail_url,
1432 get_template_directory_uri(),
1433 get_stylesheet_directory_uri()
1434 );
1435 }
1436
1437 return $_wp_random_header;
1438}
1439
1440/**
1441 * Gets random header image URL from registered images in theme.
1442 *
1443 * @since 3.2.0
1444 *
1445 * @return string Path to header image.
1446 */
1447function get_random_header_image() {
1448 $random_image = _get_random_header_data();
1449
1450 if ( empty( $random_image->url ) ) {
1451 return '';
1452 }
1453
1454 return $random_image->url;
1455}
1456
1457/**
1458 * Checks if random header image is in use.
1459 *
1460 * Always true if user expressly chooses the option in Appearance > Header.
1461 * Also true if theme has multiple header images registered, no specific header image
1462 * is chosen, and theme turns on random headers with add_theme_support().
1463 *
1464 * @since 3.2.0
1465 *
1466 * @param string $type The random pool to use. Possible values include 'any',
1467 * 'default', 'uploaded'. Default 'any'.
1468 * @return bool
1469 */
1470function is_random_header_image( $type = 'any' ) {
1471 $header_image_mod = get_theme_mod( 'header_image', get_theme_support( 'custom-header', 'default-image' ) );
1472
1473 if ( 'any' === $type ) {
1474 if ( 'random-default-image' === $header_image_mod
1475 || 'random-uploaded-image' === $header_image_mod
1476 || ( empty( $header_image_mod ) && '' !== get_random_header_image() )
1477 ) {
1478 return true;
1479 }
1480 } else {
1481 if ( "random-$type-image" === $header_image_mod ) {
1482 return true;
1483 } elseif ( 'default' === $type
1484 && empty( $header_image_mod ) && '' !== get_random_header_image()
1485 ) {
1486 return true;
1487 }
1488 }
1489
1490 return false;
1491}
1492
1493/**
1494 * Displays header image URL.
1495 *
1496 * @since 2.1.0
1497 */
1498function header_image() {
1499 $image = get_header_image();
1500
1501 if ( $image ) {
1502 echo esc_url( $image );
1503 }
1504}
1505
1506/**
1507 * Gets the header images uploaded for the active theme.
1508 *
1509 * @since 3.2.0
1510 *
1511 * @return array
1512 */
1513function get_uploaded_header_images() {
1514 $header_images = array();
1515
1516 $headers = get_posts(
1517 array(
1518 'post_type' => 'attachment',
1519 'meta_key' => '_wp_attachment_is_custom_header',
1520 'meta_value' => get_option( 'stylesheet' ),
1521 'orderby' => 'none',
1522 'nopaging' => true,
1523 )
1524 );
1525
1526 if ( empty( $headers ) ) {
1527 return array();
1528 }
1529
1530 foreach ( (array) $headers as $header ) {
1531 $url = sanitize_url( wp_get_attachment_url( $header->ID ) );
1532 $header_data = wp_get_attachment_metadata( $header->ID );
1533 $header_index = $header->ID;
1534
1535 $header_images[ $header_index ] = array();
1536 $header_images[ $header_index ]['attachment_id'] = $header->ID;
1537 $header_images[ $header_index ]['url'] = $url;
1538 $header_images[ $header_index ]['thumbnail_url'] = $url;
1539 $header_images[ $header_index ]['alt_text'] = get_post_meta( $header->ID, '_wp_attachment_image_alt', true );
1540
1541 if ( isset( $header_data['attachment_parent'] ) ) {
1542 $header_images[ $header_index ]['attachment_parent'] = $header_data['attachment_parent'];
1543 } else {
1544 $header_images[ $header_index ]['attachment_parent'] = '';
1545 }
1546
1547 if ( isset( $header_data['width'] ) ) {
1548 $header_images[ $header_index ]['width'] = $header_data['width'];
1549 }
1550 if ( isset( $header_data['height'] ) ) {
1551 $header_images[ $header_index ]['height'] = $header_data['height'];
1552 }
1553 }
1554
1555 return $header_images;
1556}
1557
1558/**
1559 * Gets the header image data.
1560 *
1561 * @since 3.4.0
1562 *
1563 * @global array $_wp_default_headers
1564 *
1565 * @return object
1566 */
1567function get_custom_header() {
1568 global $_wp_default_headers;
1569
1570 if ( is_random_header_image() ) {
1571 $data = _get_random_header_data();
1572 } else {
1573 $data = get_theme_mod( 'header_image_data' );
1574 if ( ! $data && current_theme_supports( 'custom-header', 'default-image' ) ) {
1575 $directory_args = array( get_template_directory_uri(), get_stylesheet_directory_uri() );
1576 $data = array();
1577 $data['url'] = vsprintf( get_theme_support( 'custom-header', 'default-image' ), $directory_args );
1578 $data['thumbnail_url'] = $data['url'];
1579 if ( ! empty( $_wp_default_headers ) ) {
1580 foreach ( (array) $_wp_default_headers as $default_header ) {
1581 $url = vsprintf( $default_header['url'], $directory_args );
1582 if ( $data['url'] === $url ) {
1583 $data = $default_header;
1584 $data['url'] = $url;
1585 $data['thumbnail_url'] = vsprintf( $data['thumbnail_url'], $directory_args );
1586 break;
1587 }
1588 }
1589 }
1590 }
1591 }
1592
1593 $default = array(
1594 'url' => '',
1595 'thumbnail_url' => '',
1596 'width' => get_theme_support( 'custom-header', 'width' ),
1597 'height' => get_theme_support( 'custom-header', 'height' ),
1598 'video' => get_theme_support( 'custom-header', 'video' ),
1599 );
1600 return (object) wp_parse_args( $data, $default );
1601}
1602
1603/**
1604 * Registers a selection of default headers to be displayed by the custom header admin UI.
1605 *
1606 * @since 3.0.0
1607 *
1608 * @global array $_wp_default_headers
1609 *
1610 * @param array $headers Array of headers keyed by a string ID. The IDs point to arrays
1611 * containing 'url', 'thumbnail_url', and 'description' keys.
1612 */
1613function register_default_headers( $headers ) {
1614 global $_wp_default_headers;
1615
1616 $_wp_default_headers = array_merge( (array) $_wp_default_headers, (array) $headers );
1617}
1618
1619/**
1620 * Unregisters default headers.
1621 *
1622 * This function must be called after register_default_headers() has already added the
1623 * header you want to remove.
1624 *
1625 * @see register_default_headers()
1626 * @since 3.0.0
1627 *
1628 * @global array $_wp_default_headers
1629 *
1630 * @param string|array $header The header string id (key of array) to remove, or an array thereof.
1631 * @return bool|void A single header returns true on success, false on failure.
1632 * There is currently no return value for multiple headers.
1633 */
1634function unregister_default_headers( $header ) {
1635 global $_wp_default_headers;
1636
1637 if ( is_array( $header ) ) {
1638 array_map( 'unregister_default_headers', $header );
1639 } elseif ( isset( $_wp_default_headers[ $header ] ) ) {
1640 unset( $_wp_default_headers[ $header ] );
1641 return true;
1642 } else {
1643 return false;
1644 }
1645}
1646
1647/**
1648 * Checks whether a header video is set or not.
1649 *
1650 * @since 4.7.0
1651 *
1652 * @see get_header_video_url()
1653 *
1654 * @return bool Whether a header video is set or not.
1655 */
1656function has_header_video() {
1657 return (bool) get_header_video_url();
1658}
1659
1660/**
1661 * Retrieves header video URL for custom header.
1662 *
1663 * Uses a local video if present, or falls back to an external video.
1664 *
1665 * @since 4.7.0
1666 *
1667 * @return string|false Header video URL or false if there is no video.
1668 */
1669function get_header_video_url() {
1670 $id = absint( get_theme_mod( 'header_video' ) );
1671
1672 if ( $id ) {
1673 // Get the file URL from the attachment ID.
1674 $url = wp_get_attachment_url( $id );
1675 } else {
1676 $url = get_theme_mod( 'external_header_video' );
1677 }
1678
1679 /**
1680 * Filters the header video URL.
1681 *
1682 * @since 4.7.3
1683 *
1684 * @param string $url Header video URL, if available.
1685 */
1686 $url = apply_filters( 'get_header_video_url', $url );
1687
1688 if ( ! $id && ! $url ) {
1689 return false;
1690 }
1691
1692 return sanitize_url( set_url_scheme( $url ) );
1693}
1694
1695/**
1696 * Displays header video URL.
1697 *
1698 * @since 4.7.0
1699 */
1700function the_header_video_url() {
1701 $video = get_header_video_url();
1702
1703 if ( $video ) {
1704 echo esc_url( $video );
1705 }
1706}
1707
1708/**
1709 * Retrieves header video settings.
1710 *
1711 * @since 4.7.0
1712 *
1713 * @return array
1714 */
1715function get_header_video_settings() {
1716 $header = get_custom_header();
1717 $video_url = get_header_video_url();
1718 $video_type = wp_check_filetype( $video_url, wp_get_mime_types() );
1719
1720 $settings = array(
1721 'mimeType' => '',
1722 'posterUrl' => get_header_image(),
1723 'videoUrl' => $video_url,
1724 'width' => absint( $header->width ),
1725 'height' => absint( $header->height ),
1726 'minWidth' => 900,
1727 'minHeight' => 500,
1728 'l10n' => array(
1729 'pause' => __( 'Pause' ),
1730 'play' => __( 'Play' ),
1731 'pauseSpeak' => __( 'Video is paused.' ),
1732 'playSpeak' => __( 'Video is playing.' ),
1733 ),
1734 );
1735
1736 if ( preg_match( '#^https?://(?:www\.)?(?:youtube\.com/watch|youtu\.be/)#', $video_url ) ) {
1737 $settings['mimeType'] = 'video/x-youtube';
1738 } elseif ( ! empty( $video_type['type'] ) ) {
1739 $settings['mimeType'] = $video_type['type'];
1740 }
1741
1742 /**
1743 * Filters header video settings.
1744 *
1745 * @since 4.7.0
1746 *
1747 * @param array $settings An array of header video settings.
1748 */
1749 return apply_filters( 'header_video_settings', $settings );
1750}
1751
1752/**
1753 * Checks whether a custom header is set or not.
1754 *
1755 * @since 4.7.0
1756 *
1757 * @return bool True if a custom header is set. False if not.
1758 */
1759function has_custom_header() {
1760 if ( has_header_image() || ( has_header_video() && is_header_video_active() ) ) {
1761 return true;
1762 }
1763
1764 return false;
1765}
1766
1767/**
1768 * Checks whether the custom header video is eligible to show on the current page.
1769 *
1770 * @since 4.7.0
1771 *
1772 * @return bool True if the custom header video should be shown. False if not.
1773 */
1774function is_header_video_active() {
1775 if ( ! get_theme_support( 'custom-header', 'video' ) ) {
1776 return false;
1777 }
1778
1779 $video_active_cb = get_theme_support( 'custom-header', 'video-active-callback' );
1780
1781 if ( empty( $video_active_cb ) || ! is_callable( $video_active_cb ) ) {
1782 $show_video = true;
1783 } else {
1784 $show_video = call_user_func( $video_active_cb );
1785 }
1786
1787 /**
1788 * Filters whether the custom header video is eligible to show on the current page.
1789 *
1790 * @since 4.7.0
1791 *
1792 * @param bool $show_video Whether the custom header video should be shown. Returns the value
1793 * of the theme setting for the `custom-header`'s `video-active-callback`.
1794 * If no callback is set, the default value is that of `is_front_page()`.
1795 */
1796 return apply_filters( 'is_header_video_active', $show_video );
1797}
1798
1799/**
1800 * Retrieves the markup for a custom header.
1801 *
1802 * The container div will always be returned in the Customizer preview.
1803 *
1804 * @since 4.7.0
1805 *
1806 * @return string The markup for a custom header on success.
1807 */
1808function get_custom_header_markup() {
1809 if ( ! has_custom_header() && ! is_customize_preview() ) {
1810 return '';
1811 }
1812
1813 return sprintf(
1814 '<div id="wp-custom-header" class="wp-custom-header">%s</div>',
1815 get_header_image_tag()
1816 );
1817}
1818
1819/**
1820 * Prints the markup for a custom header.
1821 *
1822 * A container div will always be printed in the Customizer preview.
1823 *
1824 * @since 4.7.0
1825 */
1826function the_custom_header_markup() {
1827 $custom_header = get_custom_header_markup();
1828 if ( empty( $custom_header ) ) {
1829 return;
1830 }
1831
1832 echo $custom_header;
1833
1834 if ( is_header_video_active() && ( has_header_video() || is_customize_preview() ) ) {
1835 wp_enqueue_script( 'wp-custom-header' );
1836 wp_localize_script( 'wp-custom-header', '_wpCustomHeaderSettings', get_header_video_settings() );
1837 }
1838}
1839
1840/**
1841 * Retrieves background image for custom background.
1842 *
1843 * @since 3.0.0
1844 *
1845 * @return string
1846 */
1847function get_background_image() {
1848 return get_theme_mod( 'background_image', get_theme_support( 'custom-background', 'default-image' ) );
1849}
1850
1851/**
1852 * Displays background image path.
1853 *
1854 * @since 3.0.0
1855 */
1856function background_image() {
1857 echo get_background_image();
1858}
1859
1860/**
1861 * Retrieves value for custom background color.
1862 *
1863 * @since 3.0.0
1864 *
1865 * @return string
1866 */
1867function get_background_color() {
1868 return get_theme_mod( 'background_color', get_theme_support( 'custom-background', 'default-color' ) );
1869}
1870
1871/**
1872 * Displays background color value.
1873 *
1874 * @since 3.0.0
1875 */
1876function background_color() {
1877 echo get_background_color();
1878}
1879
1880/**
1881 * Default custom background callback.
1882 *
1883 * @since 3.0.0
1884 */
1885function _custom_background_cb() {
1886 // $background is the saved custom image, or the default image.
1887 $background = set_url_scheme( get_background_image() );
1888
1889 /*
1890 * $color is the saved custom color.
1891 * A default has to be specified in style.css. It will not be printed here.
1892 */
1893 $color = get_background_color();
1894
1895 if ( get_theme_support( 'custom-background', 'default-color' ) === $color ) {
1896 $color = false;
1897 }
1898
1899 $type_attr = current_theme_supports( 'html5', 'style' ) ? '' : ' type="text/css"';
1900
1901 if ( ! $background && ! $color ) {
1902 if ( is_customize_preview() ) {
1903 printf( '<style%s id="custom-background-css"></style>', $type_attr );
1904 }
1905 return;
1906 }
1907
1908 $style = $color ? 'background-color: ' . maybe_hash_hex_color( $color ) . ';' : '';
1909
1910 if ( $background ) {
1911 $image = ' background-image: url("' . sanitize_url( $background ) . '");';
1912
1913 // Background Position.
1914 $position_x = get_theme_mod( 'background_position_x', get_theme_support( 'custom-background', 'default-position-x' ) );
1915 $position_y = get_theme_mod( 'background_position_y', get_theme_support( 'custom-background', 'default-position-y' ) );
1916
1917 if ( ! in_array( $position_x, array( 'left', 'center', 'right' ), true ) ) {
1918 $position_x = 'left';
1919 }
1920
1921 if ( ! in_array( $position_y, array( 'top', 'center', 'bottom' ), true ) ) {
1922 $position_y = 'top';
1923 }
1924
1925 $position = " background-position: $position_x $position_y;";
1926
1927 // Background Size.
1928 $size = get_theme_mod( 'background_size', get_theme_support( 'custom-background', 'default-size' ) );
1929
1930 if ( ! in_array( $size, array( 'auto', 'contain', 'cover' ), true ) ) {
1931 $size = 'auto';
1932 }
1933
1934 $size = " background-size: $size;";
1935
1936 // Background Repeat.
1937 $repeat = get_theme_mod( 'background_repeat', get_theme_support( 'custom-background', 'default-repeat' ) );
1938
1939 if ( ! in_array( $repeat, array( 'repeat-x', 'repeat-y', 'repeat', 'no-repeat' ), true ) ) {
1940 $repeat = 'repeat';
1941 }
1942
1943 $repeat = " background-repeat: $repeat;";
1944
1945 // Background Scroll.
1946 $attachment = get_theme_mod( 'background_attachment', get_theme_support( 'custom-background', 'default-attachment' ) );
1947
1948 if ( 'fixed' !== $attachment ) {
1949 $attachment = 'scroll';
1950 }
1951
1952 $attachment = " background-attachment: $attachment;";
1953
1954 $style .= $image . $position . $size . $repeat . $attachment;
1955 }
1956 ?>
1957<style<?php echo $type_attr; ?> id="custom-background-css">
1958body.custom-background { <?php echo trim( $style ); ?> }
1959</style>
1960 <?php
1961}
1962
1963/**
1964 * Renders the Custom CSS style element.
1965 *
1966 * @since 4.7.0
1967 */
1968function wp_custom_css_cb() {
1969 $styles = wp_get_custom_css();
1970 if ( $styles || is_customize_preview() ) :
1971 $type_attr = current_theme_supports( 'html5', 'style' ) ? '' : ' type="text/css"';
1972 ?>
1973 <style<?php echo $type_attr; ?> id="wp-custom-css">
1974 <?php
1975 // Note that esc_html() cannot be used because `div &gt; span` is not interpreted properly.
1976 echo strip_tags( $styles );
1977 ?>
1978 </style>
1979 <?php
1980 endif;
1981}
1982
1983/**
1984 * Fetches the `custom_css` post for a given theme.
1985 *
1986 * @since 4.7.0
1987 *
1988 * @param string $stylesheet Optional. A theme object stylesheet name. Defaults to the active theme.
1989 * @return WP_Post|null The custom_css post or null if none exists.
1990 */
1991function wp_get_custom_css_post( $stylesheet = '' ) {
1992 if ( empty( $stylesheet ) ) {
1993 $stylesheet = get_stylesheet();
1994 }
1995
1996 $custom_css_query_vars = array(
1997 'post_type' => 'custom_css',
1998 'post_status' => get_post_stati(),
1999 'name' => sanitize_title( $stylesheet ),
2000 'posts_per_page' => 1,
2001 'no_found_rows' => true,
2002 'cache_results' => true,
2003 'update_post_meta_cache' => false,
2004 'update_post_term_cache' => false,
2005 'lazy_load_term_meta' => false,
2006 );
2007
2008 $post = null;
2009 if ( get_stylesheet() === $stylesheet ) {
2010 $post_id = get_theme_mod( 'custom_css_post_id' );
2011
2012 if ( $post_id > 0 && get_post( $post_id ) ) {
2013 $post = get_post( $post_id );
2014 }
2015
2016 // `-1` indicates no post exists; no query necessary.
2017 if ( ! $post && -1 !== $post_id ) {
2018 $query = new WP_Query( $custom_css_query_vars );
2019 $post = $query->post;
2020 /*
2021 * Cache the lookup. See wp_update_custom_css_post().
2022 * @todo This should get cleared if a custom_css post is added/removed.
2023 */
2024 set_theme_mod( 'custom_css_post_id', $post ? $post->ID : -1 );
2025 }
2026 } else {
2027 $query = new WP_Query( $custom_css_query_vars );
2028 $post = $query->post;
2029 }
2030
2031 return $post;
2032}
2033
2034/**
2035 * Fetches the saved Custom CSS content for rendering.
2036 *
2037 * @since 4.7.0
2038 *
2039 * @param string $stylesheet Optional. A theme object stylesheet name. Defaults to the active theme.
2040 * @return string The Custom CSS Post content.
2041 */
2042function wp_get_custom_css( $stylesheet = '' ) {
2043 $css = '';
2044
2045 if ( empty( $stylesheet ) ) {
2046 $stylesheet = get_stylesheet();
2047 }
2048
2049 $post = wp_get_custom_css_post( $stylesheet );
2050 if ( $post ) {
2051 $css = $post->post_content;
2052 }
2053
2054 /**
2055 * Filters the custom CSS output into the head element.
2056 *
2057 * @since 4.7.0
2058 *
2059 * @param string $css CSS pulled in from the Custom CSS post type.
2060 * @param string $stylesheet The theme stylesheet name.
2061 */
2062 $css = apply_filters( 'wp_get_custom_css', $css, $stylesheet );
2063
2064 return $css;
2065}
2066
2067/**
2068 * Updates the `custom_css` post for a given theme.
2069 *
2070 * Inserts a `custom_css` post when one doesn't yet exist.
2071 *
2072 * @since 4.7.0
2073 *
2074 * @param string $css CSS, stored in `post_content`.
2075 * @param array $args {
2076 * Args.
2077 *
2078 * @type string $preprocessed Optional. Pre-processed CSS, stored in `post_content_filtered`.
2079 * Normally empty string.
2080 * @type string $stylesheet Optional. Stylesheet (child theme) to update.
2081 * Defaults to active theme/stylesheet.
2082 * }
2083 * @return WP_Post|WP_Error Post on success, error on failure.
2084 */
2085function wp_update_custom_css_post( $css, $args = array() ) {
2086 $args = wp_parse_args(
2087 $args,
2088 array(
2089 'preprocessed' => '',
2090 'stylesheet' => get_stylesheet(),
2091 )
2092 );
2093
2094 $data = array(
2095 'css' => $css,
2096 'preprocessed' => $args['preprocessed'],
2097 );
2098
2099 /**
2100 * Filters the `css` (`post_content`) and `preprocessed` (`post_content_filtered`) args
2101 * for a `custom_css` post being updated.
2102 *
2103 * This filter can be used by plugin that offer CSS pre-processors, to store the original
2104 * pre-processed CSS in `post_content_filtered` and then store processed CSS in `post_content`.
2105 * When used in this way, the `post_content_filtered` should be supplied as the setting value
2106 * instead of `post_content` via a the `customize_value_custom_css` filter, for example:
2107 *
2108 * <code>
2109 * add_filter( 'customize_value_custom_css', function( $value, $setting ) {
2110 * $post = wp_get_custom_css_post( $setting->stylesheet );
2111 * if ( $post && ! empty( $post->post_content_filtered ) ) {
2112 * $css = $post->post_content_filtered;
2113 * }
2114 * return $css;
2115 * }, 10, 2 );
2116 * </code>
2117 *
2118 * @since 4.7.0
2119 * @param array $data {
2120 * Custom CSS data.
2121 *
2122 * @type string $css CSS stored in `post_content`.
2123 * @type string $preprocessed Pre-processed CSS stored in `post_content_filtered`.
2124 * Normally empty string.
2125 * }
2126 * @param array $args {
2127 * The args passed into `wp_update_custom_css_post()` merged with defaults.
2128 *
2129 * @type string $css The original CSS passed in to be updated.
2130 * @type string $preprocessed The original preprocessed CSS passed in to be updated.
2131 * @type string $stylesheet The stylesheet (theme) being updated.
2132 * }
2133 */
2134 $data = apply_filters( 'update_custom_css_data', $data, array_merge( $args, compact( 'css' ) ) );
2135
2136 $post_data = array(
2137 'post_title' => $args['stylesheet'],
2138 'post_name' => sanitize_title( $args['stylesheet'] ),
2139 'post_type' => 'custom_css',
2140 'post_status' => 'publish',
2141 'post_content' => $data['css'],
2142 'post_content_filtered' => $data['preprocessed'],
2143 );
2144
2145 // Update post if it already exists, otherwise create a new one.
2146 $post = wp_get_custom_css_post( $args['stylesheet'] );
2147 if ( $post ) {
2148 $post_data['ID'] = $post->ID;
2149 $r = wp_update_post( wp_slash( $post_data ), true );
2150 } else {
2151 $r = wp_insert_post( wp_slash( $post_data ), true );
2152
2153 if ( ! is_wp_error( $r ) ) {
2154 if ( get_stylesheet() === $args['stylesheet'] ) {
2155 set_theme_mod( 'custom_css_post_id', $r );
2156 }
2157
2158 // Trigger creation of a revision. This should be removed once #30854 is resolved.
2159 $revisions = wp_get_latest_revision_id_and_total_count( $r );
2160 if ( ! is_wp_error( $revisions ) && 0 === $revisions['count'] ) {
2161 wp_save_post_revision( $r );
2162 }
2163 }
2164 }
2165
2166 if ( is_wp_error( $r ) ) {
2167 return $r;
2168 }
2169 return get_post( $r );
2170}
2171
2172/**
2173 * Adds callback for custom TinyMCE editor stylesheets.
2174 *
2175 * The parameter $stylesheet is the name of the stylesheet, relative to
2176 * the theme root. It also accepts an array of stylesheets.
2177 * It is optional and defaults to 'editor-style.css'.
2178 *
2179 * This function automatically adds another stylesheet with -rtl prefix, e.g. editor-style-rtl.css.
2180 * If that file doesn't exist, it is removed before adding the stylesheet(s) to TinyMCE.
2181 * If an array of stylesheets is passed to add_editor_style(),
2182 * RTL is only added for the first stylesheet.
2183 *
2184 * Since version 3.4 the TinyMCE body has .rtl CSS class.
2185 * It is a better option to use that class and add any RTL styles to the main stylesheet.
2186 *
2187 * @since 3.0.0
2188 *
2189 * @global array $editor_styles
2190 *
2191 * @param array|string $stylesheet Optional. Stylesheet name or array thereof, relative to theme root.
2192 * Defaults to 'editor-style.css'
2193 */
2194function add_editor_style( $stylesheet = 'editor-style.css' ) {
2195 global $editor_styles;
2196
2197 add_theme_support( 'editor-style' );
2198
2199 $editor_styles = (array) $editor_styles;
2200 $stylesheet = (array) $stylesheet;
2201
2202 if ( is_rtl() ) {
2203 $rtl_stylesheet = str_replace( '.css', '-rtl.css', $stylesheet[0] );
2204 $stylesheet[] = $rtl_stylesheet;
2205 }
2206
2207 $editor_styles = array_merge( $editor_styles, $stylesheet );
2208}
2209
2210/**
2211 * Removes all visual editor stylesheets.
2212 *
2213 * @since 3.1.0
2214 *
2215 * @global array $editor_styles
2216 *
2217 * @return bool True on success, false if there were no stylesheets to remove.
2218 */
2219function remove_editor_styles() {
2220 if ( ! current_theme_supports( 'editor-style' ) ) {
2221 return false;
2222 }
2223 _remove_theme_support( 'editor-style' );
2224 if ( is_admin() ) {
2225 $GLOBALS['editor_styles'] = array();
2226 }
2227 return true;
2228}
2229
2230/**
2231 * Retrieves any registered editor stylesheet URLs.
2232 *
2233 * @since 4.0.0
2234 *
2235 * @global array $editor_styles Registered editor stylesheets
2236 *
2237 * @return string[] If registered, a list of editor stylesheet URLs.
2238 */
2239function get_editor_stylesheets() {
2240 $stylesheets = array();
2241 // Load editor_style.css if the active theme supports it.
2242 if ( ! empty( $GLOBALS['editor_styles'] ) && is_array( $GLOBALS['editor_styles'] ) ) {
2243 $editor_styles = $GLOBALS['editor_styles'];
2244
2245 $editor_styles = array_unique( array_filter( $editor_styles ) );
2246 $style_uri = get_stylesheet_directory_uri();
2247 $style_dir = get_stylesheet_directory();
2248
2249 // Support externally referenced styles (like, say, fonts).
2250 foreach ( $editor_styles as $key => $file ) {
2251 if ( preg_match( '~^(https?:)?//~', $file ) ) {
2252 $stylesheets[] = sanitize_url( $file );
2253 unset( $editor_styles[ $key ] );
2254 }
2255 }
2256
2257 // Look in a parent theme first, that way child theme CSS overrides.
2258 if ( is_child_theme() ) {
2259 $template_uri = get_template_directory_uri();
2260 $template_dir = get_template_directory();
2261
2262 foreach ( $editor_styles as $key => $file ) {
2263 if ( $file && file_exists( "$template_dir/$file" ) ) {
2264 $stylesheets[] = "$template_uri/$file";
2265 }
2266 }
2267 }
2268
2269 foreach ( $editor_styles as $file ) {
2270 if ( $file && file_exists( "$style_dir/$file" ) ) {
2271 $stylesheets[] = "$style_uri/$file";
2272 }
2273 }
2274 }
2275
2276 /**
2277 * Filters the array of URLs of stylesheets applied to the editor.
2278 *
2279 * @since 4.3.0
2280 *
2281 * @param string[] $stylesheets Array of URLs of stylesheets to be applied to the editor.
2282 */
2283 return apply_filters( 'editor_stylesheets', $stylesheets );
2284}
2285
2286/**
2287 * Expands a theme's starter content configuration using core-provided data.
2288 *
2289 * @since 4.7.0
2290 *
2291 * @return array Array of starter content.
2292 */
2293function get_theme_starter_content() {
2294 $theme_support = get_theme_support( 'starter-content' );
2295 if ( is_array( $theme_support ) && ! empty( $theme_support[0] ) && is_array( $theme_support[0] ) ) {
2296 $config = $theme_support[0];
2297 } else {
2298 $config = array();
2299 }
2300
2301 $core_content = array(
2302 'widgets' => array(
2303 'text_business_info' => array(
2304 'text',
2305 array(
2306 'title' => _x( 'Find Us', 'Theme starter content' ),
2307 'text' => implode(
2308 '',
2309 array(
2310 '<strong>' . _x( 'Address', 'Theme starter content' ) . "</strong>\n",
2311 _x( '123 Main Street', 'Theme starter content' ) . "\n",
2312 _x( 'New York, NY 10001', 'Theme starter content' ) . "\n\n",
2313 '<strong>' . _x( 'Hours', 'Theme starter content' ) . "</strong>\n",
2314 _x( 'Monday&ndash;Friday: 9:00AM&ndash;5:00PM', 'Theme starter content' ) . "\n",
2315 _x( 'Saturday &amp; Sunday: 11:00AM&ndash;3:00PM', 'Theme starter content' ),
2316 )
2317 ),
2318 'filter' => true,
2319 'visual' => true,
2320 ),
2321 ),
2322 'text_about' => array(
2323 'text',
2324 array(
2325 'title' => _x( 'About This Site', 'Theme starter content' ),
2326 'text' => _x( 'This may be a good place to introduce yourself and your site or include some credits.', 'Theme starter content' ),
2327 'filter' => true,
2328 'visual' => true,
2329 ),
2330 ),
2331 'archives' => array(
2332 'archives',
2333 array(
2334 'title' => _x( 'Archives', 'Theme starter content' ),
2335 ),
2336 ),
2337 'calendar' => array(
2338 'calendar',
2339 array(
2340 'title' => _x( 'Calendar', 'Theme starter content' ),
2341 ),
2342 ),
2343 'categories' => array(
2344 'categories',
2345 array(
2346 'title' => _x( 'Categories', 'Theme starter content' ),
2347 ),
2348 ),
2349 'meta' => array(
2350 'meta',
2351 array(
2352 'title' => _x( 'Meta', 'Theme starter content' ),
2353 ),
2354 ),
2355 'recent-comments' => array(
2356 'recent-comments',
2357 array(
2358 'title' => _x( 'Recent Comments', 'Theme starter content' ),
2359 ),
2360 ),
2361 'recent-posts' => array(
2362 'recent-posts',
2363 array(
2364 'title' => _x( 'Recent Posts', 'Theme starter content' ),
2365 ),
2366 ),
2367 'search' => array(
2368 'search',
2369 array(
2370 'title' => _x( 'Search', 'Theme starter content' ),
2371 ),
2372 ),
2373 ),
2374 'nav_menus' => array(
2375 'link_home' => array(
2376 'type' => 'custom',
2377 'title' => _x( 'Home', 'Theme starter content' ),
2378 'url' => home_url( '/' ),
2379 ),
2380 'page_home' => array( // Deprecated in favor of 'link_home'.
2381 'type' => 'post_type',
2382 'object' => 'page',
2383 'object_id' => '{{home}}',
2384 ),
2385 'page_about' => array(
2386 'type' => 'post_type',
2387 'object' => 'page',
2388 'object_id' => '{{about}}',
2389 ),
2390 'page_blog' => array(
2391 'type' => 'post_type',
2392 'object' => 'page',
2393 'object_id' => '{{blog}}',
2394 ),
2395 'page_news' => array(
2396 'type' => 'post_type',
2397 'object' => 'page',
2398 'object_id' => '{{news}}',
2399 ),
2400 'page_contact' => array(
2401 'type' => 'post_type',
2402 'object' => 'page',
2403 'object_id' => '{{contact}}',
2404 ),
2405
2406 'link_email' => array(
2407 'title' => _x( 'Email', 'Theme starter content' ),
2408 'url' => 'mailto:wordpress@example.com',
2409 ),
2410 'link_facebook' => array(
2411 'title' => _x( 'Facebook', 'Theme starter content' ),
2412 'url' => 'https://www.facebook.com/wordpress',
2413 ),
2414 'link_foursquare' => array(
2415 'title' => _x( 'Foursquare', 'Theme starter content' ),
2416 'url' => 'https://foursquare.com/',
2417 ),
2418 'link_github' => array(
2419 'title' => _x( 'GitHub', 'Theme starter content' ),
2420 'url' => 'https://github.com/wordpress/',
2421 ),
2422 'link_instagram' => array(
2423 'title' => _x( 'Instagram', 'Theme starter content' ),
2424 'url' => 'https://www.instagram.com/explore/tags/wordcamp/',
2425 ),
2426 'link_linkedin' => array(
2427 'title' => _x( 'LinkedIn', 'Theme starter content' ),
2428 'url' => 'https://www.linkedin.com/company/1089783',
2429 ),
2430 'link_pinterest' => array(
2431 'title' => _x( 'Pinterest', 'Theme starter content' ),
2432 'url' => 'https://www.pinterest.com/',
2433 ),
2434 'link_twitter' => array(
2435 'title' => _x( 'Twitter', 'Theme starter content' ),
2436 'url' => 'https://twitter.com/wordpress',
2437 ),
2438 'link_yelp' => array(
2439 'title' => _x( 'Yelp', 'Theme starter content' ),
2440 'url' => 'https://www.yelp.com',
2441 ),
2442 'link_youtube' => array(
2443 'title' => _x( 'YouTube', 'Theme starter content' ),
2444 'url' => 'https://www.youtube.com/channel/UCdof4Ju7amm1chz1gi1T2ZA',
2445 ),
2446 ),
2447 'posts' => array(
2448 'home' => array(
2449 'post_type' => 'page',
2450 'post_title' => _x( 'Home', 'Theme starter content' ),
2451 'post_content' => sprintf(
2452 "<!-- wp:paragraph -->\n<p>%s</p>\n<!-- /wp:paragraph -->",
2453 _x( 'Welcome to your site! This is your homepage, which is what most visitors will see when they come to your site for the first time.', 'Theme starter content' )
2454 ),
2455 ),
2456 'about' => array(
2457 'post_type' => 'page',
2458 'post_title' => _x( 'About', 'Theme starter content' ),
2459 'post_content' => sprintf(
2460 "<!-- wp:paragraph -->\n<p>%s</p>\n<!-- /wp:paragraph -->",
2461 _x( 'You might be an artist who would like to introduce yourself and your work here or maybe you are a business with a mission to describe.', 'Theme starter content' )
2462 ),
2463 ),
2464 'contact' => array(
2465 'post_type' => 'page',
2466 'post_title' => _x( 'Contact', 'Theme starter content' ),
2467 'post_content' => sprintf(
2468 "<!-- wp:paragraph -->\n<p>%s</p>\n<!-- /wp:paragraph -->",
2469 _x( 'This is a page with some basic contact information, such as an address and phone number. You might also try a plugin to add a contact form.', 'Theme starter content' )
2470 ),
2471 ),
2472 'blog' => array(
2473 'post_type' => 'page',
2474 'post_title' => _x( 'Blog', 'Theme starter content' ),
2475 ),
2476 'news' => array(
2477 'post_type' => 'page',
2478 'post_title' => _x( 'News', 'Theme starter content' ),
2479 ),
2480
2481 'homepage-section' => array(
2482 'post_type' => 'page',
2483 'post_title' => _x( 'A homepage section', 'Theme starter content' ),
2484 'post_content' => sprintf(
2485 "<!-- wp:paragraph -->\n<p>%s</p>\n<!-- /wp:paragraph -->",
2486 _x( 'This is an example of a homepage section. Homepage sections can be any page other than the homepage itself, including the page that shows your latest blog posts.', 'Theme starter content' )
2487 ),
2488 ),
2489 ),
2490 );
2491
2492 $content = array();
2493
2494 foreach ( $config as $type => $args ) {
2495 switch ( $type ) {
2496 // Use options and theme_mods as-is.
2497 case 'options':
2498 case 'theme_mods':
2499 $content[ $type ] = $config[ $type ];
2500 break;
2501
2502 // Widgets are grouped into sidebars.
2503 case 'widgets':
2504 foreach ( $config[ $type ] as $sidebar_id => $widgets ) {
2505 foreach ( $widgets as $id => $widget ) {
2506 if ( is_array( $widget ) ) {
2507
2508 // Item extends core content.
2509 if ( ! empty( $core_content[ $type ][ $id ] ) ) {
2510 $widget = array(
2511 $core_content[ $type ][ $id ][0],
2512 array_merge( $core_content[ $type ][ $id ][1], $widget ),
2513 );
2514 }
2515
2516 $content[ $type ][ $sidebar_id ][] = $widget;
2517 } elseif ( is_string( $widget )
2518 && ! empty( $core_content[ $type ] )
2519 && ! empty( $core_content[ $type ][ $widget ] )
2520 ) {
2521 $content[ $type ][ $sidebar_id ][] = $core_content[ $type ][ $widget ];
2522 }
2523 }
2524 }
2525 break;
2526
2527 // And nav menu items are grouped into nav menus.
2528 case 'nav_menus':
2529 foreach ( $config[ $type ] as $nav_menu_location => $nav_menu ) {
2530
2531 // Ensure nav menus get a name.
2532 if ( empty( $nav_menu['name'] ) ) {
2533 $nav_menu['name'] = $nav_menu_location;
2534 }
2535
2536 $content[ $type ][ $nav_menu_location ]['name'] = $nav_menu['name'];
2537
2538 foreach ( $nav_menu['items'] as $id => $nav_menu_item ) {
2539 if ( is_array( $nav_menu_item ) ) {
2540
2541 // Item extends core content.
2542 if ( ! empty( $core_content[ $type ][ $id ] ) ) {
2543 $nav_menu_item = array_merge( $core_content[ $type ][ $id ], $nav_menu_item );
2544 }
2545
2546 $content[ $type ][ $nav_menu_location ]['items'][] = $nav_menu_item;
2547 } elseif ( is_string( $nav_menu_item )
2548 && ! empty( $core_content[ $type ] )
2549 && ! empty( $core_content[ $type ][ $nav_menu_item ] )
2550 ) {
2551 $content[ $type ][ $nav_menu_location ]['items'][] = $core_content[ $type ][ $nav_menu_item ];
2552 }
2553 }
2554 }
2555 break;
2556
2557 // Attachments are posts but have special treatment.
2558 case 'attachments':
2559 foreach ( $config[ $type ] as $id => $item ) {
2560 if ( ! empty( $item['file'] ) ) {
2561 $content[ $type ][ $id ] = $item;
2562 }
2563 }
2564 break;
2565
2566 /*
2567 * All that's left now are posts (besides attachments).
2568 * Not a default case for the sake of clarity and future work.
2569 */
2570 case 'posts':
2571 foreach ( $config[ $type ] as $id => $item ) {
2572 if ( is_array( $item ) ) {
2573
2574 // Item extends core content.
2575 if ( ! empty( $core_content[ $type ][ $id ] ) ) {
2576 $item = array_merge( $core_content[ $type ][ $id ], $item );
2577 }
2578
2579 // Enforce a subset of fields.
2580 $content[ $type ][ $id ] = wp_array_slice_assoc(
2581 $item,
2582 array(
2583 'post_type',
2584 'post_title',
2585 'post_excerpt',
2586 'post_name',
2587 'post_content',
2588 'menu_order',
2589 'comment_status',
2590 'thumbnail',
2591 'template',
2592 )
2593 );
2594 } elseif ( is_string( $item ) && ! empty( $core_content[ $type ][ $item ] ) ) {
2595 $content[ $type ][ $item ] = $core_content[ $type ][ $item ];
2596 }
2597 }
2598 break;
2599 }
2600 }
2601
2602 /**
2603 * Filters the expanded array of starter content.
2604 *
2605 * @since 4.7.0
2606 *
2607 * @param array $content Array of starter content.
2608 * @param array $config Array of theme-specific starter content configuration.
2609 */
2610 return apply_filters( 'get_theme_starter_content', $content, $config );
2611}
2612
2613/**
2614 * Registers theme support for a given feature.
2615 *
2616 * Must be called in the theme's functions.php file to work.
2617 * If attached to a hook, it must be {@see 'after_setup_theme'}.
2618 * The {@see 'init'} hook may be too late for some features.
2619 *
2620 * Example usage:
2621 *
2622 * add_theme_support( 'title-tag' );
2623 * add_theme_support( 'custom-logo', array(
2624 * 'height' => 480,
2625 * 'width' => 720,
2626 * ) );
2627 *
2628 * @since 2.9.0
2629 * @since 3.4.0 The `custom-header-uploads` feature was deprecated.
2630 * @since 3.6.0 The `html5` feature was added.
2631 * @since 3.6.1 The `html5` feature requires an array of types to be passed. Defaults to
2632 * 'comment-list', 'comment-form', 'search-form' for backward compatibility.
2633 * @since 3.9.0 The `html5` feature now also accepts 'gallery' and 'caption'.
2634 * @since 4.1.0 The `title-tag` feature was added.
2635 * @since 4.5.0 The `customize-selective-refresh-widgets` feature was added.
2636 * @since 4.7.0 The `starter-content` feature was added.
2637 * @since 5.0.0 The `responsive-embeds`, `align-wide`, `dark-editor-style`, `disable-custom-colors`,
2638 * `disable-custom-font-sizes`, `editor-color-palette`, `editor-font-sizes`,
2639 * `editor-styles`, and `wp-block-styles` features were added.
2640 * @since 5.3.0 The `html5` feature now also accepts 'script' and 'style'.
2641 * @since 5.3.0 Formalized the existing and already documented `...$args` parameter
2642 * by adding it to the function signature.
2643 * @since 5.4.0 The `disable-custom-gradients` feature limits to default gradients or gradients added
2644 * through `editor-gradient-presets` theme support.
2645 * @since 5.5.0 The `core-block-patterns` feature was added and is enabled by default.
2646 * @since 5.5.0 The `custom-logo` feature now also accepts 'unlink-homepage-logo'.
2647 * @since 5.6.0 The `post-formats` feature warns if no array is passed as the second parameter.
2648 * @since 5.8.0 The `widgets-block-editor` feature enables the Widgets block editor.
2649 * @since 5.8.0 The `block-templates` feature indicates whether a theme uses block-based templates.
2650 * @since 6.0.0 The `html5` feature warns if no array is passed as the second parameter.
2651 * @since 6.1.0 The `block-template-parts` feature allows to edit any reusable template part from site editor.
2652 * @since 6.1.0 The `disable-layout-styles` feature disables the default layout styles.
2653 * @since 6.3.0 The `link-color` feature allows to enable the link color setting.
2654 * @since 6.3.0 The `border` feature allows themes without theme.json to add border styles to blocks.
2655 * @since 6.5.0 The `appearance-tools` feature enables a few design tools for blocks,
2656 * see `WP_Theme_JSON::APPEARANCE_TOOLS_OPT_INS` for a complete list.
2657 * @since 6.6.0 The `editor-spacing-sizes` feature was added.
2658 *
2659 * @global array $_wp_theme_features
2660 *
2661 * @param string $feature The feature being added. Likely core values include:
2662 * - 'admin-bar'
2663 * - 'align-wide'
2664 * - 'appearance-tools'
2665 * - 'automatic-feed-links'
2666 * - 'block-templates'
2667 * - 'block-template-parts'
2668 * - 'border'
2669 * - 'core-block-patterns'
2670 * - 'custom-background'
2671 * - 'custom-header'
2672 * - 'custom-line-height'
2673 * - 'custom-logo'
2674 * - 'customize-selective-refresh-widgets'
2675 * - 'custom-spacing'
2676 * - 'custom-units'
2677 * - 'dark-editor-style'
2678 * - 'disable-custom-colors'
2679 * - 'disable-custom-font-sizes'
2680 * - 'disable-custom-gradients'
2681 * - 'disable-layout-styles'
2682 * - 'editor-color-palette'
2683 * - 'editor-gradient-presets'
2684 * - 'editor-font-sizes'
2685 * - 'editor-spacing-sizes'
2686 * - 'editor-styles'
2687 * - 'featured-content'
2688 * - 'html5'
2689 * - 'link-color'
2690 * - 'menus'
2691 * - 'post-formats'
2692 * - 'post-thumbnails'
2693 * - 'responsive-embeds'
2694 * - 'starter-content'
2695 * - 'title-tag'
2696 * - 'widgets'
2697 * - 'widgets-block-editor'
2698 * - 'wp-block-styles'
2699 * @param mixed ...$args Optional extra arguments to pass along with certain features.
2700 * @return void|false Void on success, false on failure.
2701 */
2702function add_theme_support( $feature, ...$args ) {
2703 global $_wp_theme_features;
2704
2705 if ( ! $args ) {
2706 $args = true;
2707 }
2708
2709 switch ( $feature ) {
2710 case 'post-thumbnails':
2711 // All post types are already supported.
2712 if ( true === get_theme_support( 'post-thumbnails' ) ) {
2713 return;
2714 }
2715
2716 /*
2717 * Merge post types with any that already declared their support
2718 * for post thumbnails.
2719 */
2720 if ( isset( $args[0] ) && is_array( $args[0] ) && isset( $_wp_theme_features['post-thumbnails'] ) ) {
2721 $args[0] = array_unique( array_merge( $_wp_theme_features['post-thumbnails'][0], $args[0] ) );
2722 }
2723
2724 break;
2725
2726 case 'post-formats':
2727 if ( isset( $args[0] ) && is_array( $args[0] ) ) {
2728 $post_formats = get_post_format_slugs();
2729 unset( $post_formats['standard'] );
2730
2731 $args[0] = array_intersect( $args[0], array_keys( $post_formats ) );
2732 } else {
2733 _doing_it_wrong(
2734 "add_theme_support( 'post-formats' )",
2735 __( 'You need to pass an array of post formats.' ),
2736 '5.6.0'
2737 );
2738 return false;
2739 }
2740 break;
2741
2742 case 'html5':
2743 // You can't just pass 'html5', you need to pass an array of types.
2744 if ( empty( $args[0] ) || ! is_array( $args[0] ) ) {
2745 _doing_it_wrong(
2746 "add_theme_support( 'html5' )",
2747 __( 'You need to pass an array of types.' ),
2748 '3.6.1'
2749 );
2750
2751 if ( ! empty( $args[0] ) && ! is_array( $args[0] ) ) {
2752 return false;
2753 }
2754
2755 // Build an array of types for back-compat.
2756 $args = array( 0 => array( 'comment-list', 'comment-form', 'search-form' ) );
2757 }
2758
2759 // Calling 'html5' again merges, rather than overwrites.
2760 if ( isset( $_wp_theme_features['html5'] ) ) {
2761 $args[0] = array_merge( $_wp_theme_features['html5'][0], $args[0] );
2762 }
2763 break;
2764
2765 case 'custom-logo':
2766 if ( true === $args ) {
2767 $args = array( 0 => array() );
2768 }
2769 $defaults = array(
2770 'width' => null,
2771 'height' => null,
2772 'flex-width' => false,
2773 'flex-height' => false,
2774 'header-text' => '',
2775 'unlink-homepage-logo' => false,
2776 );
2777 $args[0] = wp_parse_args( array_intersect_key( $args[0], $defaults ), $defaults );
2778
2779 // Allow full flexibility if no size is specified.
2780 if ( is_null( $args[0]['width'] ) && is_null( $args[0]['height'] ) ) {
2781 $args[0]['flex-width'] = true;
2782 $args[0]['flex-height'] = true;
2783 }
2784 break;
2785
2786 case 'custom-header-uploads':
2787 return add_theme_support( 'custom-header', array( 'uploads' => true ) );
2788
2789 case 'custom-header':
2790 if ( true === $args ) {
2791 $args = array( 0 => array() );
2792 }
2793
2794 $defaults = array(
2795 'default-image' => '',
2796 'random-default' => false,
2797 'width' => 0,
2798 'height' => 0,
2799 'flex-height' => false,
2800 'flex-width' => false,
2801 'default-text-color' => '',
2802 'header-text' => true,
2803 'uploads' => true,
2804 'wp-head-callback' => '',
2805 'admin-head-callback' => '',
2806 'admin-preview-callback' => '',
2807 'video' => false,
2808 'video-active-callback' => 'is_front_page',
2809 );
2810
2811 $jit = isset( $args[0]['__jit'] );
2812 unset( $args[0]['__jit'] );
2813
2814 /*
2815 * Merge in data from previous add_theme_support() calls.
2816 * The first value registered wins. (A child theme is set up first.)
2817 */
2818 if ( isset( $_wp_theme_features['custom-header'] ) ) {
2819 $args[0] = wp_parse_args( $_wp_theme_features['custom-header'][0], $args[0] );
2820 }
2821
2822 /*
2823 * Load in the defaults at the end, as we need to insure first one wins.
2824 * This will cause all constants to be defined, as each arg will then be set to the default.
2825 */
2826 if ( $jit ) {
2827 $args[0] = wp_parse_args( $args[0], $defaults );
2828 }
2829
2830 /*
2831 * If a constant was defined, use that value. Otherwise, define the constant to ensure
2832 * the constant is always accurate (and is not defined later, overriding our value).
2833 * As stated above, the first value wins.
2834 * Once we get to wp_loaded (just-in-time), define any constants we haven't already.
2835 * Constants should be avoided. Don't reference them. This is just for backward compatibility.
2836 */
2837
2838 if ( defined( 'NO_HEADER_TEXT' ) ) {
2839 $args[0]['header-text'] = ! NO_HEADER_TEXT;
2840 } elseif ( isset( $args[0]['header-text'] ) ) {
2841 define( 'NO_HEADER_TEXT', empty( $args[0]['header-text'] ) );
2842 }
2843
2844 if ( defined( 'HEADER_IMAGE_WIDTH' ) ) {
2845 $args[0]['width'] = (int) HEADER_IMAGE_WIDTH;
2846 } elseif ( isset( $args[0]['width'] ) ) {
2847 define( 'HEADER_IMAGE_WIDTH', (int) $args[0]['width'] );
2848 }
2849
2850 if ( defined( 'HEADER_IMAGE_HEIGHT' ) ) {
2851 $args[0]['height'] = (int) HEADER_IMAGE_HEIGHT;
2852 } elseif ( isset( $args[0]['height'] ) ) {
2853 define( 'HEADER_IMAGE_HEIGHT', (int) $args[0]['height'] );
2854 }
2855
2856 if ( defined( 'HEADER_TEXTCOLOR' ) ) {
2857 $args[0]['default-text-color'] = HEADER_TEXTCOLOR;
2858 } elseif ( isset( $args[0]['default-text-color'] ) ) {
2859 define( 'HEADER_TEXTCOLOR', $args[0]['default-text-color'] );
2860 }
2861
2862 if ( defined( 'HEADER_IMAGE' ) ) {
2863 $args[0]['default-image'] = HEADER_IMAGE;
2864 } elseif ( isset( $args[0]['default-image'] ) ) {
2865 define( 'HEADER_IMAGE', $args[0]['default-image'] );
2866 }
2867
2868 if ( $jit && ! empty( $args[0]['default-image'] ) ) {
2869 $args[0]['random-default'] = false;
2870 }
2871
2872 /*
2873 * If headers are supported, and we still don't have a defined width or height,
2874 * we have implicit flex sizes.
2875 */
2876 if ( $jit ) {
2877 if ( empty( $args[0]['width'] ) && empty( $args[0]['flex-width'] ) ) {
2878 $args[0]['flex-width'] = true;
2879 }
2880 if ( empty( $args[0]['height'] ) && empty( $args[0]['flex-height'] ) ) {
2881 $args[0]['flex-height'] = true;
2882 }
2883 }
2884
2885 break;
2886
2887 case 'custom-background':
2888 if ( true === $args ) {
2889 $args = array( 0 => array() );
2890 }
2891
2892 $defaults = array(
2893 'default-image' => '',
2894 'default-preset' => 'default',
2895 'default-position-x' => 'left',
2896 'default-position-y' => 'top',
2897 'default-size' => 'auto',
2898 'default-repeat' => 'repeat',
2899 'default-attachment' => 'scroll',
2900 'default-color' => '',
2901 'wp-head-callback' => '_custom_background_cb',
2902 'admin-head-callback' => '',
2903 'admin-preview-callback' => '',
2904 );
2905
2906 $jit = isset( $args[0]['__jit'] );
2907 unset( $args[0]['__jit'] );
2908
2909 // Merge in data from previous add_theme_support() calls. The first value registered wins.
2910 if ( isset( $_wp_theme_features['custom-background'] ) ) {
2911 $args[0] = wp_parse_args( $_wp_theme_features['custom-background'][0], $args[0] );
2912 }
2913
2914 if ( $jit ) {
2915 $args[0] = wp_parse_args( $args[0], $defaults );
2916 }
2917
2918 if ( defined( 'BACKGROUND_COLOR' ) ) {
2919 $args[0]['default-color'] = BACKGROUND_COLOR;
2920 } elseif ( isset( $args[0]['default-color'] ) || $jit ) {
2921 define( 'BACKGROUND_COLOR', $args[0]['default-color'] );
2922 }
2923
2924 if ( defined( 'BACKGROUND_IMAGE' ) ) {
2925 $args[0]['default-image'] = BACKGROUND_IMAGE;
2926 } elseif ( isset( $args[0]['default-image'] ) || $jit ) {
2927 define( 'BACKGROUND_IMAGE', $args[0]['default-image'] );
2928 }
2929
2930 break;
2931
2932 // Ensure that 'title-tag' is accessible in the admin.
2933 case 'title-tag':
2934 // Can be called in functions.php but must happen before wp_loaded, i.e. not in header.php.
2935 if ( did_action( 'wp_loaded' ) ) {
2936 _doing_it_wrong(
2937 "add_theme_support( 'title-tag' )",
2938 sprintf(
2939 /* translators: 1: title-tag, 2: wp_loaded */
2940 __( 'Theme support for %1$s should be registered before the %2$s hook.' ),
2941 '<code>title-tag</code>',
2942 '<code>wp_loaded</code>'
2943 ),
2944 '4.1.0'
2945 );
2946
2947 return false;
2948 }
2949 }
2950
2951 $_wp_theme_features[ $feature ] = $args;
2952}
2953
2954/**
2955 * Registers the internal custom header and background routines.
2956 *
2957 * @since 3.4.0
2958 * @access private
2959 *
2960 * @global Custom_Image_Header $custom_image_header
2961 * @global Custom_Background $custom_background
2962 */
2963function _custom_header_background_just_in_time() {
2964 global $custom_image_header, $custom_background;
2965
2966 if ( current_theme_supports( 'custom-header' ) ) {
2967 // In case any constants were defined after an add_custom_image_header() call, re-run.
2968 add_theme_support( 'custom-header', array( '__jit' => true ) );
2969
2970 $args = get_theme_support( 'custom-header' );
2971 if ( $args[0]['wp-head-callback'] ) {
2972 add_action( 'wp_head', $args[0]['wp-head-callback'] );
2973 }
2974
2975 if ( is_admin() ) {
2976 require_once ABSPATH . 'wp-admin/includes/class-custom-image-header.php';
2977 $custom_image_header = new Custom_Image_Header( $args[0]['admin-head-callback'], $args[0]['admin-preview-callback'] );
2978 }
2979 }
2980
2981 if ( current_theme_supports( 'custom-background' ) ) {
2982 // In case any constants were defined after an add_custom_background() call, re-run.
2983 add_theme_support( 'custom-background', array( '__jit' => true ) );
2984
2985 $args = get_theme_support( 'custom-background' );
2986 add_action( 'wp_head', $args[0]['wp-head-callback'] );
2987
2988 if ( is_admin() ) {
2989 require_once ABSPATH . 'wp-admin/includes/class-custom-background.php';
2990 $custom_background = new Custom_Background( $args[0]['admin-head-callback'], $args[0]['admin-preview-callback'] );
2991 }
2992 }
2993}
2994
2995/**
2996 * Adds CSS to hide header text for custom logo, based on Customizer setting.
2997 *
2998 * @since 4.5.0
2999 * @access private
3000 */
3001function _custom_logo_header_styles() {
3002 if ( ! current_theme_supports( 'custom-header', 'header-text' )
3003 && get_theme_support( 'custom-logo', 'header-text' )
3004 && ! get_theme_mod( 'header_text', true )
3005 ) {
3006 $classes = (array) get_theme_support( 'custom-logo', 'header-text' );
3007 $classes = array_map( 'sanitize_html_class', $classes );
3008 $classes = '.' . implode( ', .', $classes );
3009
3010 $type_attr = current_theme_supports( 'html5', 'style' ) ? '' : ' type="text/css"';
3011 ?>
3012 <!-- Custom Logo: hide header text -->
3013 <style id="custom-logo-css"<?php echo $type_attr; ?>>
3014 <?php echo $classes; ?> {
3015 position: absolute;
3016 clip-path: inset(50%);
3017 }
3018 </style>
3019 <?php
3020 }
3021}
3022
3023/**
3024 * Gets the theme support arguments passed when registering that support.
3025 *
3026 * Example usage:
3027 *
3028 * get_theme_support( 'custom-logo' );
3029 * get_theme_support( 'custom-header', 'width' );
3030 *
3031 * @since 3.1.0
3032 * @since 5.3.0 Formalized the existing and already documented `...$args` parameter
3033 * by adding it to the function signature.
3034 *
3035 * @global array $_wp_theme_features
3036 *
3037 * @param string $feature The feature to check. See add_theme_support() for the list
3038 * of possible values.
3039 * @param mixed ...$args Optional extra arguments to be checked against certain features.
3040 * @return mixed The array of extra arguments or the value for the registered feature.
3041 */
3042function get_theme_support( $feature, ...$args ) {
3043 global $_wp_theme_features;
3044
3045 if ( ! isset( $_wp_theme_features[ $feature ] ) ) {
3046 return false;
3047 }
3048
3049 if ( ! $args ) {
3050 return $_wp_theme_features[ $feature ];
3051 }
3052
3053 switch ( $feature ) {
3054 case 'custom-logo':
3055 case 'custom-header':
3056 case 'custom-background':
3057 if ( isset( $_wp_theme_features[ $feature ][0][ $args[0] ] ) ) {
3058 return $_wp_theme_features[ $feature ][0][ $args[0] ];
3059 }
3060 return false;
3061
3062 default:
3063 return $_wp_theme_features[ $feature ];
3064 }
3065}
3066
3067/**
3068 * Allows a theme to de-register its support of a certain feature
3069 *
3070 * Should be called in the theme's functions.php file. Generally would
3071 * be used for child themes to override support from the parent theme.
3072 *
3073 * @since 3.0.0
3074 *
3075 * @see add_theme_support()
3076 *
3077 * @param string $feature The feature being removed. See add_theme_support() for the list
3078 * of possible values.
3079 * @return bool|void Whether feature was removed.
3080 */
3081function remove_theme_support( $feature ) {
3082 // Do not remove internal registrations that are not used directly by themes.
3083 if ( in_array( $feature, array( 'editor-style', 'widgets', 'menus' ), true ) ) {
3084 return false;
3085 }
3086
3087 return _remove_theme_support( $feature );
3088}
3089
3090/**
3091 * Do not use. Removes theme support internally without knowledge of those not used
3092 * by themes directly.
3093 *
3094 * @access private
3095 * @since 3.1.0
3096 * @global array $_wp_theme_features
3097 * @global Custom_Image_Header $custom_image_header
3098 * @global Custom_Background $custom_background
3099 *
3100 * @param string $feature The feature being removed. See add_theme_support() for the list
3101 * of possible values.
3102 * @return bool True if support was removed, false if the feature was not registered.
3103 */
3104function _remove_theme_support( $feature ) {
3105 global $_wp_theme_features;
3106
3107 switch ( $feature ) {
3108 case 'custom-header-uploads':
3109 if ( ! isset( $_wp_theme_features['custom-header'] ) ) {
3110 return false;
3111 }
3112 add_theme_support( 'custom-header', array( 'uploads' => false ) );
3113 return; // Do not continue - custom-header-uploads no longer exists.
3114 }
3115
3116 if ( ! isset( $_wp_theme_features[ $feature ] ) ) {
3117 return false;
3118 }
3119
3120 switch ( $feature ) {
3121 case 'custom-header':
3122 if ( ! did_action( 'wp_loaded' ) ) {
3123 break;
3124 }
3125 $support = get_theme_support( 'custom-header' );
3126 if ( isset( $support[0]['wp-head-callback'] ) ) {
3127 remove_action( 'wp_head', $support[0]['wp-head-callback'] );
3128 }
3129 if ( isset( $GLOBALS['custom_image_header'] ) ) {
3130 remove_action( 'admin_menu', array( $GLOBALS['custom_image_header'], 'init' ) );
3131 unset( $GLOBALS['custom_image_header'] );
3132 }
3133 break;
3134
3135 case 'custom-background':
3136 if ( ! did_action( 'wp_loaded' ) ) {
3137 break;
3138 }
3139 $support = get_theme_support( 'custom-background' );
3140 if ( isset( $support[0]['wp-head-callback'] ) ) {
3141 remove_action( 'wp_head', $support[0]['wp-head-callback'] );
3142 }
3143 remove_action( 'admin_menu', array( $GLOBALS['custom_background'], 'init' ) );
3144 unset( $GLOBALS['custom_background'] );
3145 break;
3146 }
3147
3148 unset( $_wp_theme_features[ $feature ] );
3149
3150 return true;
3151}
3152
3153/**
3154 * Checks a theme's support for a given feature.
3155 *
3156 * Example usage:
3157 *
3158 * current_theme_supports( 'custom-logo' );
3159 * current_theme_supports( 'html5', 'comment-form' );
3160 *
3161 * @since 2.9.0
3162 * @since 5.3.0 Formalized the existing and already documented `...$args` parameter
3163 * by adding it to the function signature.
3164 *
3165 * @global array $_wp_theme_features
3166 *
3167 * @param string $feature The feature being checked. See add_theme_support() for the list
3168 * of possible values.
3169 * @param mixed ...$args Optional extra arguments to be checked against certain features.
3170 * @return bool True if the active theme supports the feature, false otherwise.
3171 */
3172function current_theme_supports( $feature, ...$args ) {
3173 global $_wp_theme_features;
3174
3175 if ( 'custom-header-uploads' === $feature ) {
3176 return current_theme_supports( 'custom-header', 'uploads' );
3177 }
3178
3179 if ( ! isset( $_wp_theme_features[ $feature ] ) ) {
3180 return false;
3181 }
3182
3183 // If no args passed then no extra checks need to be performed.
3184 if ( ! $args ) {
3185 /** This filter is documented in wp-includes/theme.php */
3186 return apply_filters( "current_theme_supports-{$feature}", true, $args, $_wp_theme_features[ $feature ] ); // phpcs:ignore WordPress.NamingConventions.ValidHookName.UseUnderscores
3187 }
3188
3189 switch ( $feature ) {
3190 case 'post-thumbnails':
3191 /*
3192 * post-thumbnails can be registered for only certain content/post types
3193 * by passing an array of types to add_theme_support().
3194 * If no array was passed, then any type is accepted.
3195 */
3196 if ( true === $_wp_theme_features[ $feature ] ) { // Registered for all types.
3197 return true;
3198 }
3199 $content_type = $args[0];
3200 return in_array( $content_type, $_wp_theme_features[ $feature ][0], true );
3201
3202 case 'html5':
3203 case 'post-formats':
3204 /*
3205 * Specific post formats can be registered by passing an array of types
3206 * to add_theme_support().
3207 *
3208 * Specific areas of HTML5 support *must* be passed via an array to add_theme_support().
3209 */
3210 $type = $args[0];
3211 return in_array( $type, $_wp_theme_features[ $feature ][0], true );
3212
3213 case 'custom-logo':
3214 case 'custom-header':
3215 case 'custom-background':
3216 // Specific capabilities can be registered by passing an array to add_theme_support().
3217 return ( isset( $_wp_theme_features[ $feature ][0][ $args[0] ] ) && $_wp_theme_features[ $feature ][0][ $args[0] ] );
3218 }
3219
3220 /**
3221 * Filters whether the active theme supports a specific feature.
3222 *
3223 * The dynamic portion of the hook name, `$feature`, refers to the specific
3224 * theme feature. See add_theme_support() for the list of possible values.
3225 *
3226 * @since 3.4.0
3227 *
3228 * @param bool $supports Whether the active theme supports the given feature. Default true.
3229 * @param array $args Array of arguments for the feature.
3230 * @param string $feature The theme feature.
3231 */
3232 return apply_filters( "current_theme_supports-{$feature}", true, $args, $_wp_theme_features[ $feature ] ); // phpcs:ignore WordPress.NamingConventions.ValidHookName.UseUnderscores
3233}
3234
3235/**
3236 * Checks a theme's support for a given feature before loading the functions which implement it.
3237 *
3238 * @since 2.9.0
3239 *
3240 * @param string $feature The feature being checked. See add_theme_support() for the list
3241 * of possible values.
3242 * @param string $file Path to the file.
3243 * @return bool True if the active theme supports the supplied feature, false otherwise.
3244 */
3245function require_if_theme_supports( $feature, $file ) {
3246 if ( current_theme_supports( $feature ) ) {
3247 require $file;
3248 return true;
3249 }
3250 return false;
3251}
3252
3253/**
3254 * Registers a theme feature for use in add_theme_support().
3255 *
3256 * This does not indicate that the active theme supports the feature, it only describes
3257 * the feature's supported options.
3258 *
3259 * @since 5.5.0
3260 *
3261 * @see add_theme_support()
3262 *
3263 * @global array $_wp_registered_theme_features
3264 *
3265 * @param string $feature The name uniquely identifying the feature. See add_theme_support()
3266 * for the list of possible values.
3267 * @param array $args {
3268 * Data used to describe the theme.
3269 *
3270 * @type string $type The type of data associated with this feature.
3271 * Valid values are 'string', 'boolean', 'integer',
3272 * 'number', 'array', and 'object'. Defaults to 'boolean'.
3273 * @type bool $variadic Does this feature utilize the variadic support
3274 * of add_theme_support(), or are all arguments specified
3275 * as the second parameter. Must be used with the "array" type.
3276 * @type string $description A short description of the feature. Included in
3277 * the Themes REST API schema. Intended for developers.
3278 * @type bool|array $show_in_rest {
3279 * Whether this feature should be included in the Themes REST API endpoint.
3280 * Defaults to not being included. When registering an 'array' or 'object' type,
3281 * this argument must be an array with the 'schema' key.
3282 *
3283 * @type array $schema Specifies the JSON Schema definition describing
3284 * the feature. If any objects in the schema do not include
3285 * the 'additionalProperties' keyword, it is set to false.
3286 * @type string $name An alternate name to be used as the property name
3287 * in the REST API.
3288 * @type callable $prepare_callback A function used to format the theme support in the REST API.
3289 * Receives the raw theme support value.
3290 * }
3291 * }
3292 * @return true|WP_Error True if the theme feature was successfully registered, a WP_Error object if not.
3293 */
3294function register_theme_feature( $feature, $args = array() ) {
3295 global $_wp_registered_theme_features;
3296
3297 if ( ! is_array( $_wp_registered_theme_features ) ) {
3298 $_wp_registered_theme_features = array();
3299 }
3300
3301 $defaults = array(
3302 'type' => 'boolean',
3303 'variadic' => false,
3304 'description' => '',
3305 'show_in_rest' => false,
3306 );
3307
3308 $args = wp_parse_args( $args, $defaults );
3309
3310 if ( true === $args['show_in_rest'] ) {
3311 $args['show_in_rest'] = array();
3312 }
3313
3314 if ( is_array( $args['show_in_rest'] ) ) {
3315 $args['show_in_rest'] = wp_parse_args(
3316 $args['show_in_rest'],
3317 array(
3318 'schema' => array(),
3319 'name' => $feature,
3320 'prepare_callback' => null,
3321 )
3322 );
3323 }
3324
3325 if ( ! in_array( $args['type'], array( 'string', 'boolean', 'integer', 'number', 'array', 'object' ), true ) ) {
3326 return new WP_Error(
3327 'invalid_type',
3328 __( 'The feature "type" is not valid JSON Schema type.' )
3329 );
3330 }
3331
3332 if ( true === $args['variadic'] && 'array' !== $args['type'] ) {
3333 return new WP_Error(
3334 'variadic_must_be_array',
3335 __( 'When registering a "variadic" theme feature, the "type" must be an "array".' )
3336 );
3337 }
3338
3339 if ( false !== $args['show_in_rest'] && in_array( $args['type'], array( 'array', 'object' ), true ) ) {
3340 if ( ! is_array( $args['show_in_rest'] ) || empty( $args['show_in_rest']['schema'] ) ) {
3341 return new WP_Error(
3342 'missing_schema',
3343 __( 'When registering an "array" or "object" feature to show in the REST API, the feature\'s schema must also be defined.' )
3344 );
3345 }
3346
3347 if ( 'array' === $args['type'] && ! isset( $args['show_in_rest']['schema']['items'] ) ) {
3348 return new WP_Error(
3349 'missing_schema_items',
3350 __( 'When registering an "array" feature, the feature\'s schema must include the "items" keyword.' )
3351 );
3352 }
3353
3354 if ( 'object' === $args['type'] && ! isset( $args['show_in_rest']['schema']['properties'] ) ) {
3355 return new WP_Error(
3356 'missing_schema_properties',
3357 __( 'When registering an "object" feature, the feature\'s schema must include the "properties" keyword.' )
3358 );
3359 }
3360 }
3361
3362 if ( is_array( $args['show_in_rest'] ) ) {
3363 if ( isset( $args['show_in_rest']['prepare_callback'] )
3364 && ! is_callable( $args['show_in_rest']['prepare_callback'] )
3365 ) {
3366 return new WP_Error(
3367 'invalid_rest_prepare_callback',
3368 sprintf(
3369 /* translators: %s: prepare_callback */
3370 __( 'The "%s" must be a callable function.' ),
3371 'prepare_callback'
3372 )
3373 );
3374 }
3375
3376 $args['show_in_rest']['schema'] = wp_parse_args(
3377 $args['show_in_rest']['schema'],
3378 array(
3379 'description' => $args['description'],
3380 'type' => $args['type'],
3381 'default' => false,
3382 )
3383 );
3384
3385 if ( is_bool( $args['show_in_rest']['schema']['default'] )
3386 && ! in_array( 'boolean', (array) $args['show_in_rest']['schema']['type'], true )
3387 ) {
3388 // Automatically include the "boolean" type when the default value is a boolean.
3389 $args['show_in_rest']['schema']['type'] = (array) $args['show_in_rest']['schema']['type'];
3390 array_unshift( $args['show_in_rest']['schema']['type'], 'boolean' );
3391 }
3392
3393 $args['show_in_rest']['schema'] = rest_default_additional_properties_to_false( $args['show_in_rest']['schema'] );
3394 }
3395
3396 $_wp_registered_theme_features[ $feature ] = $args;
3397
3398 return true;
3399}
3400
3401/**
3402 * Gets the list of registered theme features.
3403 *
3404 * @since 5.5.0
3405 *
3406 * @global array $_wp_registered_theme_features
3407 *
3408 * @return array[] List of theme features, keyed by their name.
3409 */
3410function get_registered_theme_features() {
3411 global $_wp_registered_theme_features;
3412
3413 if ( ! is_array( $_wp_registered_theme_features ) ) {
3414 return array();
3415 }
3416
3417 return $_wp_registered_theme_features;
3418}
3419
3420/**
3421 * Gets the registration config for a theme feature.
3422 *
3423 * @since 5.5.0
3424 *
3425 * @global array $_wp_registered_theme_features
3426 *
3427 * @param string $feature The feature name. See add_theme_support() for the list
3428 * of possible values.
3429 * @return array|null The registration args, or null if the feature was not registered.
3430 */
3431function get_registered_theme_feature( $feature ) {
3432 global $_wp_registered_theme_features;
3433
3434 if ( ! is_array( $_wp_registered_theme_features ) ) {
3435 return null;
3436 }
3437
3438 return isset( $_wp_registered_theme_features[ $feature ] ) ? $_wp_registered_theme_features[ $feature ] : null;
3439}
3440
3441/**
3442 * Checks an attachment being deleted to see if it's a header or background image.
3443 *
3444 * If true it removes the theme modification which would be pointing at the deleted
3445 * attachment.
3446 *
3447 * @access private
3448 * @since 3.0.0
3449 * @since 4.3.0 Also removes `header_image_data`.
3450 * @since 4.5.0 Also removes custom logo theme mods.
3451 * @since 6.6.0 Also removes `site_logo` option set by the site logo block.
3452 *
3453 * @param int $id The attachment ID.
3454 */
3455function _delete_attachment_theme_mod( $id ) {
3456 $attachment_image = wp_get_attachment_url( $id );
3457 $header_image = get_header_image();
3458 $background_image = get_background_image();
3459 $custom_logo_id = (int) get_theme_mod( 'custom_logo' );
3460 $site_logo_id = (int) get_option( 'site_logo' );
3461
3462 if ( $custom_logo_id && $custom_logo_id === $id ) {
3463 remove_theme_mod( 'custom_logo' );
3464 remove_theme_mod( 'header_text' );
3465 }
3466
3467 if ( $site_logo_id && $site_logo_id === $id ) {
3468 delete_option( 'site_logo' );
3469 }
3470
3471 if ( $header_image && $header_image === $attachment_image ) {
3472 remove_theme_mod( 'header_image' );
3473 remove_theme_mod( 'header_image_data' );
3474 }
3475
3476 if ( $background_image && $background_image === $attachment_image ) {
3477 remove_theme_mod( 'background_image' );
3478 }
3479}
3480
3481/**
3482 * Checks if a theme has been changed and runs 'after_switch_theme' hook on the next WP load.
3483 *
3484 * See {@see 'after_switch_theme'}.
3485 *
3486 * @since 3.3.0
3487 */
3488function check_theme_switched() {
3489 $stylesheet = get_option( 'theme_switched' );
3490
3491 if ( $stylesheet ) {
3492 $old_theme = wp_get_theme( $stylesheet );
3493
3494 // Prevent widget & menu mapping from running since Customizer already called it up front.
3495 if ( get_option( 'theme_switched_via_customizer' ) ) {
3496 remove_action( 'after_switch_theme', '_wp_menus_changed' );
3497 remove_action( 'after_switch_theme', '_wp_sidebars_changed' );
3498 update_option( 'theme_switched_via_customizer', false );
3499 }
3500
3501 if ( $old_theme->exists() ) {
3502 /**
3503 * Fires on the next WP load after the theme has been switched.
3504 *
3505 * The parameters differ according to whether the old theme exists or not.
3506 * If the old theme is missing, the old name will instead be the slug
3507 * of the old theme.
3508 *
3509 * See {@see 'switch_theme'}.
3510 *
3511 * @since 3.3.0
3512 *
3513 * @param string $old_name Old theme name.
3514 * @param WP_Theme $old_theme WP_Theme instance of the old theme.
3515 */
3516 do_action( 'after_switch_theme', $old_theme->get( 'Name' ), $old_theme );
3517 } else {
3518 /** This action is documented in wp-includes/theme.php */
3519 do_action( 'after_switch_theme', $stylesheet, $old_theme );
3520 }
3521
3522 flush_rewrite_rules();
3523
3524 update_option( 'theme_switched', false );
3525 }
3526}
3527
3528/**
3529 * Includes and instantiates the WP_Customize_Manager class.
3530 *
3531 * Loads the Customizer at plugins_loaded when accessing the customize.php admin
3532 * page or when any request includes a wp_customize=on param or a customize_changeset
3533 * param (a UUID). This param is a signal for whether to bootstrap the Customizer when
3534 * WordPress is loading, especially in the Customizer preview
3535 * or when making Customizer Ajax requests for widgets or menus.
3536 *
3537 * @since 3.4.0
3538 *
3539 * @global WP_Customize_Manager $wp_customize
3540 */
3541function _wp_customize_include() {
3542
3543 $is_customize_admin_page = ( is_admin() && 'customize.php' === basename( $_SERVER['PHP_SELF'] ) );
3544 $should_include = (
3545 $is_customize_admin_page
3546 ||
3547 ( isset( $_REQUEST['wp_customize'] ) && 'on' === $_REQUEST['wp_customize'] )
3548 ||
3549 ( ! empty( $_GET['customize_changeset_uuid'] ) || ! empty( $_POST['customize_changeset_uuid'] ) )
3550 );
3551
3552 if ( ! $should_include ) {
3553 return;
3554 }
3555
3556 /*
3557 * Note that wp_unslash() is not being used on the input vars because it is
3558 * called before wp_magic_quotes() gets called. Besides this fact, none of
3559 * the values should contain any characters needing slashes anyway.
3560 */
3561 $keys = array(
3562 'changeset_uuid',
3563 'customize_changeset_uuid',
3564 'customize_theme',
3565 'theme',
3566 'customize_messenger_channel',
3567 'customize_autosaved',
3568 );
3569 $input_vars = array_merge(
3570 wp_array_slice_assoc( $_GET, $keys ),
3571 wp_array_slice_assoc( $_POST, $keys )
3572 );
3573
3574 $theme = null;
3575 $autosaved = null;
3576 $messenger_channel = null;
3577
3578 /*
3579 * Value false indicates UUID should be determined after_setup_theme
3580 * to either re-use existing saved changeset or else generate a new UUID if none exists.
3581 */
3582 $changeset_uuid = false;
3583
3584 /*
3585 * Set initially to false since defaults to true for back-compat;
3586 * can be overridden via the customize_changeset_branching filter.
3587 */
3588 $branching = false;
3589
3590 if ( $is_customize_admin_page && isset( $input_vars['changeset_uuid'] ) ) {
3591 $changeset_uuid = sanitize_key( $input_vars['changeset_uuid'] );
3592 } elseif ( ! empty( $input_vars['customize_changeset_uuid'] ) ) {
3593 $changeset_uuid = sanitize_key( $input_vars['customize_changeset_uuid'] );
3594 }
3595
3596 // Note that theme will be sanitized via WP_Theme.
3597 if ( $is_customize_admin_page && isset( $input_vars['theme'] ) ) {
3598 $theme = $input_vars['theme'];
3599 } elseif ( isset( $input_vars['customize_theme'] ) ) {
3600 $theme = $input_vars['customize_theme'];
3601 }
3602
3603 if ( ! empty( $input_vars['customize_autosaved'] ) ) {
3604 $autosaved = true;
3605 }
3606
3607 if ( isset( $input_vars['customize_messenger_channel'] ) ) {
3608 $messenger_channel = sanitize_key( $input_vars['customize_messenger_channel'] );
3609 }
3610
3611 /*
3612 * Note that settings must be previewed even outside the customizer preview
3613 * and also in the customizer pane itself. This is to enable loading an existing
3614 * changeset into the customizer. Previewing the settings only has to be prevented
3615 * here in the case of a customize_save action because this will cause WP to think
3616 * there is nothing changed that needs to be saved.
3617 */
3618 $is_customize_save_action = (
3619 wp_doing_ajax()
3620 &&
3621 isset( $_REQUEST['action'] )
3622 &&
3623 'customize_save' === wp_unslash( $_REQUEST['action'] )
3624 );
3625 $settings_previewed = ! $is_customize_save_action;
3626
3627 require_once ABSPATH . WPINC . '/class-wp-customize-manager.php';
3628 $GLOBALS['wp_customize'] = new WP_Customize_Manager(
3629 compact(
3630 'changeset_uuid',
3631 'theme',
3632 'messenger_channel',
3633 'settings_previewed',
3634 'autosaved',
3635 'branching'
3636 )
3637 );
3638}
3639
3640/**
3641 * Publishes a snapshot's changes.
3642 *
3643 * @since 4.7.0
3644 * @access private
3645 *
3646 * @global WP_Customize_Manager $wp_customize Customizer instance.
3647 *
3648 * @param string $new_status New post status.
3649 * @param string $old_status Old post status.
3650 * @param WP_Post $changeset_post Changeset post object.
3651 */
3652function _wp_customize_publish_changeset( $new_status, $old_status, $changeset_post ) {
3653 global $wp_customize;
3654
3655 $is_publishing_changeset = (
3656 'customize_changeset' === $changeset_post->post_type
3657 &&
3658 'publish' === $new_status
3659 &&
3660 'publish' !== $old_status
3661 );
3662 if ( ! $is_publishing_changeset ) {
3663 return;
3664 }
3665
3666 if ( empty( $wp_customize ) ) {
3667 require_once ABSPATH . WPINC . '/class-wp-customize-manager.php';
3668 $wp_customize = new WP_Customize_Manager(
3669 array(
3670 'changeset_uuid' => $changeset_post->post_name,
3671 'settings_previewed' => false,
3672 )
3673 );
3674 }
3675
3676 if ( ! did_action( 'customize_register' ) ) {
3677 /*
3678 * When running from CLI or Cron, the customize_register action will need
3679 * to be triggered in order for core, themes, and plugins to register their
3680 * settings. Normally core will add_action( 'customize_register' ) at
3681 * priority 10 to register the core settings, and if any themes/plugins
3682 * also add_action( 'customize_register' ) at the same priority, they
3683 * will have a $wp_customize with those settings registered since they
3684 * call add_action() afterward, normally. However, when manually doing
3685 * the customize_register action after the setup_theme, then the order
3686 * will be reversed for two actions added at priority 10, resulting in
3687 * the core settings no longer being available as expected to themes/plugins.
3688 * So the following manually calls the method that registers the core
3689 * settings up front before doing the action.
3690 */
3691 remove_action( 'customize_register', array( $wp_customize, 'register_controls' ) );
3692 $wp_customize->register_controls();
3693
3694 /** This filter is documented in wp-includes/class-wp-customize-manager.php */
3695 do_action( 'customize_register', $wp_customize );
3696 }
3697 $wp_customize->_publish_changeset_values( $changeset_post->ID );
3698
3699 /*
3700 * Trash the changeset post if revisions are not enabled. Unpublished
3701 * changesets by default get garbage collected due to the auto-draft status.
3702 * When a changeset post is published, however, it would no longer get cleaned
3703 * out. This is a problem when the changeset posts are never displayed anywhere,
3704 * since they would just be endlessly piling up. So here we use the revisions
3705 * feature to indicate whether or not a published changeset should get trashed
3706 * and thus garbage collected.
3707 */
3708 if ( ! wp_revisions_enabled( $changeset_post ) ) {
3709 $wp_customize->trash_changeset_post( $changeset_post->ID );
3710 }
3711}
3712
3713/**
3714 * Filters changeset post data upon insert to ensure post_name is intact.
3715 *
3716 * This is needed to prevent the post_name from being dropped when the post is
3717 * transitioned into pending status by a contributor.
3718 *
3719 * @since 4.7.0
3720 *
3721 * @see wp_insert_post()
3722 *
3723 * @param array $post_data An array of slashed post data.
3724 * @param array $supplied_post_data An array of sanitized, but otherwise unmodified post data.
3725 * @return array Filtered data.
3726 */
3727function _wp_customize_changeset_filter_insert_post_data( $post_data, $supplied_post_data ) {
3728 if ( isset( $post_data['post_type'] ) && 'customize_changeset' === $post_data['post_type'] ) {
3729
3730 // Prevent post_name from being dropped, such as when contributor saves a changeset post as pending.
3731 if ( empty( $post_data['post_name'] ) && ! empty( $supplied_post_data['post_name'] ) ) {
3732 $post_data['post_name'] = $supplied_post_data['post_name'];
3733 }
3734 }
3735 return $post_data;
3736}
3737
3738/**
3739 * Adds settings for the customize-loader script.
3740 *
3741 * @since 3.4.0
3742 */
3743function _wp_customize_loader_settings() {
3744 $admin_origin = parse_url( admin_url() );
3745 $home_origin = parse_url( home_url() );
3746 $cross_domain = ( strtolower( $admin_origin['host'] ) !== strtolower( $home_origin['host'] ) );
3747
3748 $browser = array(
3749 'mobile' => wp_is_mobile(),
3750 'ios' => wp_is_mobile() && preg_match( '/iPad|iPod|iPhone/', $_SERVER['HTTP_USER_AGENT'] ),
3751 );
3752
3753 $settings = array(
3754 'url' => esc_url( admin_url( 'customize.php' ) ),
3755 'isCrossDomain' => $cross_domain,
3756 'browser' => $browser,
3757 'l10n' => array(
3758 'saveAlert' => __( 'The changes you made will be lost if you navigate away from this page.' ),
3759 'mainIframeTitle' => __( 'Customizer' ),
3760 ),
3761 );
3762
3763 $script = 'var _wpCustomizeLoaderSettings = ' . wp_json_encode( $settings, JSON_HEX_TAG | JSON_UNESCAPED_SLASHES ) . ';';
3764
3765 $wp_scripts = wp_scripts();
3766 $data = $wp_scripts->get_data( 'customize-loader', 'data' );
3767 if ( $data ) {
3768 $script = "$data\n$script";
3769 }
3770
3771 $wp_scripts->add_data( 'customize-loader', 'data', $script );
3772}
3773
3774/**
3775 * Returns a URL to load the Customizer.
3776 *
3777 * @since 3.4.0
3778 *
3779 * @param string $stylesheet Optional. Theme to customize. Defaults to active theme.
3780 * The theme's stylesheet will be urlencoded if necessary.
3781 * @return string
3782 */
3783function wp_customize_url( $stylesheet = '' ) {
3784 $url = admin_url( 'customize.php' );
3785 if ( $stylesheet ) {
3786 $url = add_query_arg( 'theme', urlencode( $stylesheet ), $url );
3787 }
3788 return esc_url( $url );
3789}
3790
3791/**
3792 * Prints a script to check whether or not the Customizer is supported,
3793 * and apply either the no-customize-support or customize-support class
3794 * to the body.
3795 *
3796 * This function MUST be called inside the body tag.
3797 *
3798 * Ideally, call this function immediately after the body tag is opened.
3799 * This prevents a flash of unstyled content.
3800 *
3801 * It is also recommended that you add the "no-customize-support" class
3802 * to the body tag by default.
3803 *
3804 * @since 3.4.0
3805 * @since 4.7.0 Support for IE8 and below is explicitly removed via conditional comments.
3806 * @since 5.5.0 IE8 and older are no longer supported.
3807 */
3808function wp_customize_support_script() {
3809 $admin_origin = parse_url( admin_url() );
3810 $home_origin = parse_url( home_url() );
3811 $cross_domain = ( strtolower( $admin_origin['host'] ) !== strtolower( $home_origin['host'] ) );
3812 ob_start();
3813 ?>
3814 <script>
3815 (function() {
3816 var request, b = document.body, c = 'className', cs = 'customize-support', rcs = new RegExp('(^|\\s+)(no-)?'+cs+'(\\s+|$)');
3817
3818 <?php if ( $cross_domain ) : ?>
3819 request = (function(){ var xhr = new XMLHttpRequest(); return ('withCredentials' in xhr); })();
3820 <?php else : ?>
3821 request = true;
3822 <?php endif; ?>
3823
3824 b[c] = b[c].replace( rcs, ' ' );
3825 // The customizer requires postMessage and CORS (if the site is cross domain).
3826 b[c] += ( window.postMessage && request ? ' ' : ' no-' ) + cs;
3827 }());
3828 </script>
3829 <?php
3830 wp_print_inline_script_tag( wp_remove_surrounding_empty_script_tags( ob_get_clean() ) . "\n//# sourceURL=" . rawurlencode( __FUNCTION__ ) );
3831}
3832
3833/**
3834 * Whether the site is being previewed in the Customizer.
3835 *
3836 * @since 4.0.0
3837 *
3838 * @global WP_Customize_Manager $wp_customize Customizer instance.
3839 *
3840 * @return bool True if the site is being previewed in the Customizer, false otherwise.
3841 */
3842function is_customize_preview() {
3843 global $wp_customize;
3844
3845 return ( $wp_customize instanceof WP_Customize_Manager ) && $wp_customize->is_preview();
3846}
3847
3848/**
3849 * Makes sure that auto-draft posts get their post_date bumped or status changed
3850 * to draft to prevent premature garbage-collection.
3851 *
3852 * When a changeset is updated but remains an auto-draft, ensure the post_date
3853 * for the auto-draft posts remains the same so that it will be
3854 * garbage-collected at the same time by `wp_delete_auto_drafts()`. Otherwise,
3855 * if the changeset is updated to be a draft then update the posts
3856 * to have a far-future post_date so that they will never be garbage collected
3857 * unless the changeset post itself is deleted.
3858 *
3859 * When a changeset is updated to be a persistent draft or to be scheduled for
3860 * publishing, then transition any dependent auto-drafts to a draft status so
3861 * that they likewise will not be garbage-collected but also so that they can
3862 * be edited in the admin before publishing since there is not yet a post/page
3863 * editing flow in the Customizer. See #39752.
3864 *
3865 * @link https://core.trac.wordpress.org/ticket/39752
3866 *
3867 * @since 4.8.0
3868 * @access private
3869 * @see wp_delete_auto_drafts()
3870 *
3871 * @global wpdb $wpdb WordPress database abstraction object.
3872 *
3873 * @param string $new_status Transition to this post status.
3874 * @param string $old_status Previous post status.
3875 * @param \WP_Post $post Post data.
3876 */
3877function _wp_keep_alive_customize_changeset_dependent_auto_drafts( $new_status, $old_status, $post ) {
3878 global $wpdb;
3879 unset( $old_status );
3880
3881 // Short-circuit if not a changeset or if the changeset was published.
3882 if ( 'customize_changeset' !== $post->post_type || 'publish' === $new_status ) {
3883 return;
3884 }
3885
3886 $data = json_decode( $post->post_content, true );
3887 if ( empty( $data['nav_menus_created_posts']['value'] ) ) {
3888 return;
3889 }
3890
3891 /*
3892 * Actually, in lieu of keeping alive, trash any customization drafts here if the changeset itself is
3893 * getting trashed. This is needed because when a changeset transitions to a draft, then any of the
3894 * dependent auto-draft post/page stubs will also get transitioned to customization drafts which
3895 * are then visible in the WP Admin. We cannot wait for the deletion of the changeset in which
3896 * _wp_delete_customize_changeset_dependent_auto_drafts() will be called, since they need to be
3897 * trashed to remove from visibility immediately.
3898 */
3899 if ( 'trash' === $new_status ) {
3900 foreach ( $data['nav_menus_created_posts']['value'] as $post_id ) {
3901 if ( ! empty( $post_id ) && 'draft' === get_post_status( $post_id ) ) {
3902 wp_trash_post( $post_id );
3903 }
3904 }
3905 return;
3906 }
3907
3908 $post_args = array();
3909 if ( 'auto-draft' === $new_status ) {
3910 /*
3911 * Keep the post date for the post matching the changeset
3912 * so that it will not be garbage-collected before the changeset.
3913 */
3914 $post_args['post_date'] = $post->post_date; // Note wp_delete_auto_drafts() only looks at this date.
3915 } else {
3916 /*
3917 * Since the changeset no longer has an auto-draft (and it is not published)
3918 * it is now a persistent changeset, a long-lived draft, and so any
3919 * associated auto-draft posts should likewise transition into having a draft
3920 * status. These drafts will be treated differently than regular drafts in
3921 * that they will be tied to the given changeset. The publish meta box is
3922 * replaced with a notice about how the post is part of a set of customized changes
3923 * which will be published when the changeset is published.
3924 */
3925 $post_args['post_status'] = 'draft';
3926 }
3927
3928 foreach ( $data['nav_menus_created_posts']['value'] as $post_id ) {
3929 if ( empty( $post_id ) || 'auto-draft' !== get_post_status( $post_id ) ) {
3930 continue;
3931 }
3932 $wpdb->update(
3933 $wpdb->posts,
3934 $post_args,
3935 array( 'ID' => $post_id )
3936 );
3937 clean_post_cache( $post_id );
3938 }
3939}
3940
3941/**
3942 * Creates the initial theme features when the 'setup_theme' action is fired.
3943 *
3944 * See {@see 'setup_theme'}.
3945 *
3946 * @since 5.5.0
3947 * @since 6.0.1 The `block-templates` feature was added.
3948 */
3949function create_initial_theme_features() {
3950 register_theme_feature(
3951 'align-wide',
3952 array(
3953 'description' => __( 'Whether theme opts in to wide alignment CSS class.' ),
3954 'show_in_rest' => true,
3955 )
3956 );
3957 register_theme_feature(
3958 'automatic-feed-links',
3959 array(
3960 'description' => __( 'Whether posts and comments RSS feed links are added to head.' ),
3961 'show_in_rest' => true,
3962 )
3963 );
3964 register_theme_feature(
3965 'block-templates',
3966 array(
3967 'description' => __( 'Whether a theme uses block-based templates.' ),
3968 'show_in_rest' => true,
3969 )
3970 );
3971 register_theme_feature(
3972 'block-template-parts',
3973 array(
3974 'description' => __( 'Whether a theme uses block-based template parts.' ),
3975 'show_in_rest' => true,
3976 )
3977 );
3978 register_theme_feature(
3979 'custom-background',
3980 array(
3981 'description' => __( 'Custom background if defined by the theme.' ),
3982 'type' => 'object',
3983 'show_in_rest' => array(
3984 'schema' => array(
3985 'properties' => array(
3986 'default-image' => array(
3987 'type' => 'string',
3988 'format' => 'uri',
3989 ),
3990 'default-preset' => array(
3991 'type' => 'string',
3992 'enum' => array(
3993 'default',
3994 'fill',
3995 'fit',
3996 'repeat',
3997 'custom',
3998 ),
3999 ),
4000 'default-position-x' => array(
4001 'type' => 'string',
4002 'enum' => array(
4003 'left',
4004 'center',
4005 'right',
4006 ),
4007 ),
4008 'default-position-y' => array(
4009 'type' => 'string',
4010 'enum' => array(
4011 'left',
4012 'center',
4013 'right',
4014 ),
4015 ),
4016 'default-size' => array(
4017 'type' => 'string',
4018 'enum' => array(
4019 'auto',
4020 'contain',
4021 'cover',
4022 ),
4023 ),
4024 'default-repeat' => array(
4025 'type' => 'string',
4026 'enum' => array(
4027 'repeat-x',
4028 'repeat-y',
4029 'repeat',
4030 'no-repeat',
4031 ),
4032 ),
4033 'default-attachment' => array(
4034 'type' => 'string',
4035 'enum' => array(
4036 'scroll',
4037 'fixed',
4038 ),
4039 ),
4040 'default-color' => array(
4041 'type' => 'string',
4042 ),
4043 ),
4044 ),
4045 ),
4046 )
4047 );
4048 register_theme_feature(
4049 'custom-header',
4050 array(
4051 'description' => __( 'Custom header if defined by the theme.' ),
4052 'type' => 'object',
4053 'show_in_rest' => array(
4054 'schema' => array(
4055 'properties' => array(
4056 'default-image' => array(
4057 'type' => 'string',
4058 'format' => 'uri',
4059 ),
4060 'random-default' => array(
4061 'type' => 'boolean',
4062 ),
4063 'width' => array(
4064 'type' => 'integer',
4065 ),
4066 'height' => array(
4067 'type' => 'integer',
4068 ),
4069 'flex-height' => array(
4070 'type' => 'boolean',
4071 ),
4072 'flex-width' => array(
4073 'type' => 'boolean',
4074 ),
4075 'default-text-color' => array(
4076 'type' => 'string',
4077 ),
4078 'header-text' => array(
4079 'type' => 'boolean',
4080 ),
4081 'uploads' => array(
4082 'type' => 'boolean',
4083 ),
4084 'video' => array(
4085 'type' => 'boolean',
4086 ),
4087 ),
4088 ),
4089 ),
4090 )
4091 );
4092 register_theme_feature(
4093 'custom-logo',
4094 array(
4095 'type' => 'object',
4096 'description' => __( 'Custom logo if defined by the theme.' ),
4097 'show_in_rest' => array(
4098 'schema' => array(
4099 'properties' => array(
4100 'width' => array(
4101 'type' => 'integer',
4102 ),
4103 'height' => array(
4104 'type' => 'integer',
4105 ),
4106 'flex-width' => array(
4107 'type' => 'boolean',
4108 ),
4109 'flex-height' => array(
4110 'type' => 'boolean',
4111 ),
4112 'header-text' => array(
4113 'type' => 'array',
4114 'items' => array(
4115 'type' => 'string',
4116 ),
4117 ),
4118 'unlink-homepage-logo' => array(
4119 'type' => 'boolean',
4120 ),
4121 ),
4122 ),
4123 ),
4124 )
4125 );
4126 register_theme_feature(
4127 'customize-selective-refresh-widgets',
4128 array(
4129 'description' => __( 'Whether the theme enables Selective Refresh for Widgets being managed with the Customizer.' ),
4130 'show_in_rest' => true,
4131 )
4132 );
4133 register_theme_feature(
4134 'dark-editor-style',
4135 array(
4136 'description' => __( 'Whether theme opts in to the dark editor style UI.' ),
4137 'show_in_rest' => true,
4138 )
4139 );
4140 register_theme_feature(
4141 'disable-custom-colors',
4142 array(
4143 'description' => __( 'Whether the theme disables custom colors.' ),
4144 'show_in_rest' => true,
4145 )
4146 );
4147 register_theme_feature(
4148 'disable-custom-font-sizes',
4149 array(
4150 'description' => __( 'Whether the theme disables custom font sizes.' ),
4151 'show_in_rest' => true,
4152 )
4153 );
4154 register_theme_feature(
4155 'disable-custom-gradients',
4156 array(
4157 'description' => __( 'Whether the theme disables custom gradients.' ),
4158 'show_in_rest' => true,
4159 )
4160 );
4161 register_theme_feature(
4162 'disable-layout-styles',
4163 array(
4164 'description' => __( 'Whether the theme disables generated layout styles.' ),
4165 'show_in_rest' => true,
4166 )
4167 );
4168 register_theme_feature(
4169 'editor-color-palette',
4170 array(
4171 'type' => 'array',
4172 'description' => __( 'Custom color palette if defined by the theme.' ),
4173 'show_in_rest' => array(
4174 'schema' => array(
4175 'items' => array(
4176 'type' => 'object',
4177 'properties' => array(
4178 'name' => array(
4179 'type' => 'string',
4180 ),
4181 'slug' => array(
4182 'type' => 'string',
4183 ),
4184 'color' => array(
4185 'type' => 'string',
4186 ),
4187 ),
4188 ),
4189 ),
4190 ),
4191 )
4192 );
4193 register_theme_feature(
4194 'editor-font-sizes',
4195 array(
4196 'type' => 'array',
4197 'description' => __( 'Custom font sizes if defined by the theme.' ),
4198 'show_in_rest' => array(
4199 'schema' => array(
4200 'items' => array(
4201 'type' => 'object',
4202 'properties' => array(
4203 'name' => array(
4204 'type' => 'string',
4205 ),
4206 'size' => array(
4207 'type' => 'number',
4208 ),
4209 'slug' => array(
4210 'type' => 'string',
4211 ),
4212 ),
4213 ),
4214 ),
4215 ),
4216 )
4217 );
4218 register_theme_feature(
4219 'editor-gradient-presets',
4220 array(
4221 'type' => 'array',
4222 'description' => __( 'Custom gradient presets if defined by the theme.' ),
4223 'show_in_rest' => array(
4224 'schema' => array(
4225 'items' => array(
4226 'type' => 'object',
4227 'properties' => array(
4228 'name' => array(
4229 'type' => 'string',
4230 ),
4231 'gradient' => array(
4232 'type' => 'string',
4233 ),
4234 'slug' => array(
4235 'type' => 'string',
4236 ),
4237 ),
4238 ),
4239 ),
4240 ),
4241 )
4242 );
4243 register_theme_feature(
4244 'editor-spacing-sizes',
4245 array(
4246 'type' => 'array',
4247 'description' => __( 'Custom spacing sizes if defined by the theme.' ),
4248 'show_in_rest' => array(
4249 'schema' => array(
4250 'items' => array(
4251 'type' => 'object',
4252 'properties' => array(
4253 'name' => array(
4254 'type' => 'string',
4255 ),
4256 'size' => array(
4257 'type' => 'string',
4258 ),
4259 'slug' => array(
4260 'type' => 'string',
4261 ),
4262 ),
4263 ),
4264 ),
4265 ),
4266 )
4267 );
4268 register_theme_feature(
4269 'editor-styles',
4270 array(
4271 'description' => __( 'Whether theme opts in to the editor styles CSS wrapper.' ),
4272 'show_in_rest' => true,
4273 )
4274 );
4275 register_theme_feature(
4276 'html5',
4277 array(
4278 'type' => 'array',
4279 'description' => __( 'Allows use of HTML5 markup for search forms, comment forms, comment lists, gallery, and caption.' ),
4280 'show_in_rest' => array(
4281 'schema' => array(
4282 'items' => array(
4283 'type' => 'string',
4284 'enum' => array(
4285 'search-form',
4286 'comment-form',
4287 'comment-list',
4288 'gallery',
4289 'caption',
4290 'script',
4291 'style',
4292 ),
4293 ),
4294 ),
4295 ),
4296 )
4297 );
4298 register_theme_feature(
4299 'post-formats',
4300 array(
4301 'type' => 'array',
4302 'description' => __( 'Post formats supported.' ),
4303 'show_in_rest' => array(
4304 'name' => 'formats',
4305 'schema' => array(
4306 'items' => array(
4307 'type' => 'string',
4308 'enum' => get_post_format_slugs(),
4309 ),
4310 'default' => array( 'standard' ),
4311 ),
4312 'prepare_callback' => static function ( $formats ) {
4313 $formats = is_array( $formats ) ? array_values( $formats[0] ) : array();
4314 $formats = array_merge( array( 'standard' ), $formats );
4315
4316 return $formats;
4317 },
4318 ),
4319 )
4320 );
4321 register_theme_feature(
4322 'post-thumbnails',
4323 array(
4324 'type' => 'array',
4325 'description' => __( 'The post types that support thumbnails or true if all post types are supported.' ),
4326 'show_in_rest' => array(
4327 'type' => array( 'boolean', 'array' ),
4328 'schema' => array(
4329 'items' => array(
4330 'type' => 'string',
4331 ),
4332 ),
4333 ),
4334 )
4335 );
4336 register_theme_feature(
4337 'responsive-embeds',
4338 array(
4339 'description' => __( 'Whether the theme supports responsive embedded content.' ),
4340 'show_in_rest' => true,
4341 )
4342 );
4343 register_theme_feature(
4344 'title-tag',
4345 array(
4346 'description' => __( 'Whether the theme can manage the document title tag.' ),
4347 'show_in_rest' => true,
4348 )
4349 );
4350 register_theme_feature(
4351 'wp-block-styles',
4352 array(
4353 'description' => __( 'Whether theme opts in to default WordPress block styles for viewing.' ),
4354 'show_in_rest' => true,
4355 )
4356 );
4357}
4358
4359/**
4360 * Returns whether the active theme is a block-based theme or not.
4361 *
4362 * @since 5.9.0
4363 *
4364 * @global string[] $wp_theme_directories
4365 *
4366 * @return bool Whether the active theme is a block-based theme or not.
4367 */
4368function wp_is_block_theme() {
4369 if ( empty( $GLOBALS['wp_theme_directories'] ) ) {
4370 _doing_it_wrong( __FUNCTION__, __( 'This function should not be called before the theme directory is registered.' ), '6.8.0' );
4371 return false;
4372 }
4373
4374 return wp_get_theme()->is_block_theme();
4375}
4376
4377/**
4378 * Given an element name, returns a class name.
4379 *
4380 * Alias of WP_Theme_JSON::get_element_class_name.
4381 *
4382 * @since 6.1.0
4383 *
4384 * @param string $element The name of the element.
4385 *
4386 * @return string The name of the class.
4387 */
4388function wp_theme_get_element_class_name( $element ) {
4389 return WP_Theme_JSON::get_element_class_name( $element );
4390}
4391
4392/**
4393 * Adds default theme supports for block themes when the 'after_setup_theme' action fires.
4394 *
4395 * See {@see 'after_setup_theme'}.
4396 *
4397 * @since 5.9.0
4398 * @access private
4399 */
4400function _add_default_theme_supports() {
4401 if ( ! wp_is_block_theme() ) {
4402 return;
4403 }
4404
4405 add_theme_support( 'post-thumbnails' );
4406 add_theme_support( 'responsive-embeds' );
4407 add_theme_support( 'editor-styles' );
4408 /*
4409 * Makes block themes support HTML5 by default for the comment block and search form
4410 * (which use default template functions) and `[caption]` and `[gallery]` shortcodes.
4411 * Other blocks contain their own HTML5 markup.
4412 */
4413 add_theme_support( 'html5', array( 'comment-form', 'comment-list', 'search-form', 'gallery', 'caption', 'style', 'script' ) );
4414 add_theme_support( 'automatic-feed-links' );
4415
4416 add_filter( 'should_load_separate_core_block_assets', '__return_true' );
4417 add_filter( 'should_load_block_assets_on_demand', '__return_true' );
4418
4419 /*
4420 * Remove the Customizer's Menus panel when block theme is active.
4421 */
4422 add_filter(
4423 'customize_panel_active',
4424 static function ( $active, WP_Customize_Panel $panel ) {
4425 if (
4426 'nav_menus' === $panel->id &&
4427 ! current_theme_supports( 'menus' ) &&
4428 ! current_theme_supports( 'widgets' )
4429 ) {
4430 $active = false;
4431 }
4432 return $active;
4433 },
4434 10,
4435 2
4436 );
4437}
4438
Ui Ux Design – Teachers Night Out https://cardgames4educators.com Wed, 16 Oct 2024 22:24:18 +0000 en-US hourly 1 https://wordpress.org/?v=6.9.4 https://cardgames4educators.com/wp-content/uploads/2024/06/cropped-Card-4-Educators-logo-32x32.png Ui Ux Design – Teachers Night Out https://cardgames4educators.com 32 32 Masters In English How English Speaker https://cardgames4educators.com/masters-in-english-how-english-speaker/ https://cardgames4educators.com/masters-in-english-how-english-speaker/#comments Mon, 27 May 2024 08:54:45 +0000 https://themexriver.com/wp/kadu/?p=1

Erat himenaeos neque id sagittis massa. Hac suscipit pulvinar dignissim platea magnis eu. Don tellus a pharetra inceptos efficitur dui pulvinar. Feugiat facilisis penatibus pulvinar nunc dictumst donec odio platea habitasse. Lacus porta dolor purus elit ante bibendum tortor netus taciti nullam cubilia. Erat per suspendisse placerat morbi egestas pulvinar bibendum sollicitudin nec. Euismod cubilia eleifend velit himenaeos sodales lectus. Leo maximus cras ac porttitor aliquam torquent pulvinar odio volutpat parturient. Quisque risus finibus suspendisse mus purus magnis facilisi condimentum consectetur dui. Curae elit suspendisse cursus vehicula.

Turpis taciti class non vel pretium quis pulvinar tempor lobortis nunc. Libero phasellus parturient sapien volutpat malesuada ornare. Cubilia dignissim sollicitudin rhoncus lacinia maximus. Cras lorem fermentum bibendum pellentesque nisl etiam ligula enim cubilia. Vulputate pede sapien torquent montes tempus malesuada in mattis dis turpis vitae. Porta est tempor ex eget feugiat vulputate ipsum. Justo nec iaculis habitant diam arcu fermentum.

We offer comprehen sive emplo ment services such as assistance wit employer compliance.Our company is your strategic HR partner as instead of HR. john smithson

Cubilia dignissim sollicitudin rhoncus lacinia maximus. Cras lorem fermentum bibendum pellentesque nisl etiam ligula enim cubilia. Vulputate pede sapien torquent montes tempus malesuada in mattis dis turpis vitae.

Exploring Learning Landscapes in Academic

Feugiat facilisis penatibus pulvinar nunc dictumst donec odio platea habitasse. Lacus porta dolor purus elit ante bibendum tortor netus taciti nullam cubilia. Erat per suspendisse placerat morbi egestas pulvinar bibendum sollicitudin nec. Euismod cubilia eleifend velit himenaeos sodales lectus. Leo maximus cras ac porttitor aliquam torquent.

]]>
https://cardgames4educators.com/masters-in-english-how-english-speaker/feed/ 1