PluginProbe ʕ •ᴥ•ʔ
Jetpack – WP Security, Backup, Speed, & Growth / 15.2.1
Jetpack – WP Security, Backup, Speed, & Growth v15.2.1
12.0.3 12.1.3 12.2.3 12.3.2 12.4.2 12.5.2 12.6.4 12.7.3 12.8.3 12.9.5 13.0.2 13.1.5 13.2.4 13.3.3 13.4.5 13.5.2 13.6.2 13.7.2 13.8.3 13.9.2 14.0.1 14.1.1 14.2.2 14.3.1 14.4.2 14.5.1 14.6.1 14.7.1 14.8.1 14.9.2 15.0.3 15.1.2 15.2.1 15.3.2 15.4.1 15.5.1 15.6.1 15.7.2 15.8.1 15.9.2 16.0.2 16.1.3 16.2-a.5 16.2-a.3 16.1.2 16.2-a.1 16.1.1 16.1 16.1-beta 16.1-beta.2 16.1-beta.3 16.1-a.5 16.1-a.3 16.0.1 16.1-a.1 16.0 16.0-beta 16.0-a.7 16.0-a.5 15.9.1 16.0-a.3 16.0-a.1 15.9 15.9-beta 15.9-a.7 15.9-a.5 15.9-a.3 15.9-a.1 15.8 15.8-beta 15.8-a.7 15.8-a.5 5.2.5 5.3.4 5.4.4 5.5.5 5.6.5 5.7.5 5.8.4 5.9.4 6.0.4 6.1 6.1.1 6.1.2 6.1.3 6.1.4 6.1.5 6.2 6.2.1 6.2.2 6.2.3 6.2.4 6.2.5 6.3 6.3.1 6.3.2 6.3.3 6.3.4 6.3.5 6.3.6 6.3.7 6.4 6.4.1 6.4.2 6.4.3 6.4.4 6.4.5 6.4.6 6.5 6.5.1 6.5.2 6.5.3 6.5.4 6.6 6.6.1 6.6.2 6.6.3 6.6.4 6.6.5 6.7 6.7.1 6.7.2 6.7.3 6.7.4 6.8 6.8.1 6.8.2 6.8.3 6.8.4 6.8.5 6.9 6.9.1 6.9.2 6.9.3 6.9.4 7.0 7.0.1 7.0.2 7.0.3 7.0.4 7.0.5 7.1 7.1.1 7.1.2 7.1.3 7.1.4 7.1.5 7.2 7.2.1 7.2.1.1 7.2.2 7.2.3 7.2.4 7.2.5 7.3 7.3.0.1 7.3.1 7.3.1.1 7.3.2 7.3.3 7.3.4 7.3.5 7.4 7.4.1 7.4.2 7.4.3 7.4.4 7.4.5 7.5 7.5.0.1 7.5.1 7.5.2 7.5.3 7.5.4 7.5.5 7.5.6 7.5.7 7.6 7.6.1 7.6.2 7.6.3 7.6.4 7.7 7.7.1 7.7.2 7.7.3 7.7.4 7.7.5 7.7.6 7.8 7.8.1 7.8.2 7.8.3 7.8.4 7.9 7.9.1 7.9.2 7.9.3 7.9.4 8.0 8.0.1 8.0.2 8.0.3 8.1 8.1.1 8.1.2 8.1.3 8.1.4 8.2 8.2.0.1 8.2.1 8.2.2 8.2.3 8.2.4 8.2.5 8.2.6 8.3 8.3.1 8.3.2 8.3.3 8.4 8.4.1 8.4.2 8.4.3 8.4.4 8.4.5 8.5 8.5.1 8.5.2 8.5.3 8.6 8.6.1 8.6.2 8.6.3 8.6.4 8.7 8.7.0.1 8.7.1 8.7.2 8.7.3 8.7.4 8.8 8.8.1 8.8.2 8.8.3 8.8.4 8.8.5 8.9 8.9.1 8.9.2 8.9.3 8.9.4 9.0 9.0.1 9.0.2 9.0.3 9.0.4 9.0.5 9.1 9.1.1 9.1.2 9.1.3 9.2 9.2.1 9.2.2 9.2.3 9.2.4 9.3 9.3.1 9.3.2 9.3.3 9.3.4 9.3.5 9.4 9.4.1 9.4.2 9.4.3 9.4.4 9.5 9.5.1 9.5.2 9.5.3 9.5.4 9.5.5 9.6 9.6.1 9.6.2 9.6.3 9.6.4 9.7 9.7.1 9.7.2 15.7-beta.2 9.7.3 15.7.1 9.8 15.8-a.1 9.8.1 15.8-a.3 9.8.2 2.0.9 9.8.3 2.1.7 9.9 2.2.10 9.9.1 2.3.10 9.9.2 2.4.7 9.9.3 2.5.5 2.6.6 2.7.5 2.8.5 2.9.6 3.0.6 3.1.5 3.2.5 3.3.6 3.4.6 3.5.6 3.6.4 3.7.5 3.8.5 3.9.10 4.0.7 4.1.4 4.2.5 4.3.5 4.4.5 4.5.3 4.6.3 4.7.4 4.8.5 4.9.3 5.0.3 5.1.4 trunk 10.0 10.0.1 10.0.2 10.1 10.1.1 10.1.2 10.2 10.2.1 10.2.2 10.2.3 10.3 10.3.1 10.3.2 10.4 10.4.1 10.4.2 10.5 10.5.1 10.5.2 10.5.3 10.6 10.6.1 10.6.2 10.7 10.7.1 10.7.2 10.8 10.8.1 10.8.2 10.9 10.9.1 10.9.2 10.9.3 11.0 11.0.1 11.0.2 11.1 11.1.1 11.1.2 11.1.3 11.1.4 11.2 11.2.1 11.2.2 11.3 11.3.1 11.3.2 11.3.3 11.3.4 11.4 11.4.1 11.4.2 11.5 11.5.1 11.5.2 11.5.3 11.6 11.6.1 11.6.2 11.7 11.7.1 11.7.2 11.7.3 11.8 11.8.3 11.8.4 11.8.5 11.8.6 11.9 11.9.1 11.9.2 11.9.3 12.0 12.0.1 12.0.2 12.1 12.1.1 12.1.2 12.2 12.2.1 12.2.2 12.3 12.3.1 12.4 12.4.1 12.5 12.5.1 12.6 12.6.1 12.6.2 12.6.3 12.7 12.7.1 12.7.2 12.8 12.8.1 12.8.2 12.9 12.9.1 12.9.2 12.9.3 12.9.4 13.0 13.0.1 13.1 13.1.1 13.1.2 13.1.3 13.1.4 13.2 13.2.1 13.2.2 13.2.3 13.3 13.3.1 13.3.2 13.4 13.4.1 13.4.2 13.4.3 13.4.4 13.5 13.5.1 13.6 13.6.1 13.7 13.7.1 13.8 13.8.1 13.8.2 13.9 13.9.1 14.0 14.1 14.2 14.2.1 14.3 14.4 14.4.1 14.5 14.6 14.7 14.8 14.9 14.9.1 15.0 15.0.1 15.0.2 15.1 15.1.1 15.2 15.3 15.3.1 15.4 15.5 15.6 15.7 15.7-a.1 15.7-a.3 15.7-a.5 15.7-a.7 15.7-beta
jetpack / modules / shortcodes / youtube.php
jetpack / modules / shortcodes Last commit date
css 10 months ago images 2 years ago img 2 years ago js 9 months ago archiveorg-book.php 1 year ago archiveorg.php 1 year ago archives.php 1 year ago bandcamp.php 11 months ago brightcove.php 1 year ago cartodb.php 1 year ago class.filter-embedded-html-objects.php 1 year ago codepen.php 1 year ago crowdsignal.php 9 months ago dailymotion.php 1 year ago descript.php 1 year ago facebook.php 1 year ago flatio.php 1 year ago flickr.php 11 months ago getty.php 1 year ago gist.php 1 year ago googleapps.php 1 year ago googlemaps.php 1 year ago googleplus.php 1 year ago gravatar.php 1 year ago houzz.php 1 year ago inline-pdfs.php 1 year ago instagram.php 1 year ago kickstarter.php 1 year ago mailchimp.php 1 year ago medium.php 1 year ago mixcloud.php 1 year ago others.php 1 year ago pinterest.php 1 year ago presentations.php 1 year ago quiz.php 1 year ago recipe.php 11 months ago scribd.php 1 year ago shortcode-utils.php 1 year ago sitemap.php 1 year ago slideshare.php 9 months ago slideshow.php 11 months ago smartframe.php 1 year ago soundcloud.php 1 year ago spotify.php 1 year ago ted.php 11 months ago tweet.php 1 year ago twitchtv.php 1 year ago twitter-timeline.php 1 year ago twitter.php 1 year ago unavailable.php 1 year ago untappd-menu.php 1 year ago upcoming-events.php 1 year ago ustream.php 1 year ago videopress.php 1 year ago vimeo.php 1 year ago vine.php 1 year ago vr.php 1 year ago wufoo.php 1 year ago youtube.php 1 year ago
youtube.php
673 lines
1 <?php
2 /**
3 * Youtube shortcode
4 *
5 * Contains shortcode + some improvements over the Core Embeds syntax (see http://codex.wordpress.org/Embeds )
6 *
7 * Examples:
8 * [youtube https://www.youtube.com/watch?v=WVbQ-oro7FQ]
9 * [youtube=http://www.youtube.com/watch?v=wq0rXGLs0YM&fs=1&hl=bg_BG&autohide=1&rel=0]
10 * http://www.youtube.com/watch?v=H2Ncxw1xfck&w=320&h=240&fmt=1&rel=0&showsearch=1&hd=0
11 * http://www.youtube.com/v/9FhMMmqzbD8?fs=1&hl=en_US
12 * https://www.youtube.com/playlist?list=PLP7HaNDU4Cifov7C2fQM8Ij6Ew_uPHEXW
13 *
14 * @package automattic/jetpack
15 */
16
17 if ( ! defined( 'ABSPATH' ) ) {
18 exit( 0 );
19 }
20
21 /**
22 * Replaces YouTube embeds with YouTube shortcodes.
23 *
24 * Covers the following formats:
25 * 2008-07-15:
26 * <object width="425" height="344"><param name="movie" value="http://www.youtube.com/v/bZBHZT3a-FA&hl=en&fs=1"></param><param name="allowFullScreen" value="true"></param><embed src="http://www.youtube.com/v/bZBHZT3a-FA&hl=en&fs=1" type="application/x-shockwave-flash" allowfullscreen="true" width="425" height="344"></embed></object>
27 * around 2008-06-06 youtube changed their old embed code to this:
28 * <object width="425" height="344"><param name="movie" value="http://www.youtube.com/v/M1D30gS7Z8U&hl=en"></param><embed src="http://www.youtube.com/v/M1D30gS7Z8U&hl=en" type="application/x-shockwave-flash" width="425" height="344"></embed></object>
29 * old style was:
30 * <object width="425" height="344"><param name="movie" value="http://www.youtube.com/v/dGY28Qbj76A&rel=0"></param><param name="wmode" value="transparent"></param><embed src="http://www.youtube.com/v/dGY28Qbj76A&rel=0" type="application/x-shockwave-flash" wmode="transparent" width="425" height="344"></embed></object>
31 * 12-2010:
32 * <object width="640" height="385"><param name="movie" value="http://www.youtube.com/v/3H8bnKdf654?fs=1&amp;hl=en_GB"></param><param name="allowFullScreen" value="true"></param><param name="allowscriptaccess" value="always"></param><embed src="http://www.youtube.com/v/3H8bnKdf654?fs=1&amp;hl=en_GB" type="application/x-shockwave-flash" allowscriptaccess="always" allowfullscreen="true" width="640" height="385"></embed></object>
33 * 01-2011:
34 * <iframe title="YouTube video player" class="youtube-player" width="640" height="390" src="http://www.youtube.com/embed/Qq9El3ki0_g" frameborder="0" allowFullScreen></iframe>
35 * <iframe class="youtube-player" width="640" height="385" src="http://www.youtube.com/embed/VIDEO_ID" frameborder="0"></iframe>
36 *
37 * @param string $content HTML content.
38 * @return string The content with YouTube embeds replaced with YouTube shortcodes.
39 */
40 function jetpack_youtube_embed_to_short_code( $content ) {
41 if ( ! is_string( $content ) || ! str_contains( $content, 'youtube.com' ) ) {
42 return $content;
43 }
44
45 // older codes.
46 $regexp = '!<object(.*?)>.*?<param\s+name=[\'"]movie[\'"]\s+value=[\'"](https?:)?//www\.youtube\.com/v/([^\'"]+)[\'"].*?>.*?</object>!i';
47 $regexp_ent = htmlspecialchars( $regexp, ENT_NOQUOTES );
48 $old_regexp = '!<embed(?:\s+\w+="[^"]*")*\s+src="https?(?:\:|&#0*58;)//www\.youtube\.com/v/([^"]+)"(?:\s+\w+="[^"]*")*\s*(?:/>|>\s*</embed>)!';
49 $old_regexp_ent = str_replace( '&amp;#0*58;', '&amp;#0*58;|&#0*58;', htmlspecialchars( $old_regexp, ENT_NOQUOTES ) );
50
51 // new code.
52 $ifr_regexp = '!<iframe((?:\s+\w+="[^"]*")*?)\s+src="(https?:)?//(?:www\.)*youtube.com/embed/([^"]+)".*?</iframe>!i';
53 $ifr_regexp_ent = str_replace( '&amp;#0*58;', '&amp;#0*58;|&#0*58;', htmlspecialchars( $ifr_regexp, ENT_NOQUOTES ) );
54
55 foreach ( compact( 'regexp', 'regexp_ent', 'old_regexp', 'old_regexp_ent', 'ifr_regexp', 'ifr_regexp_ent' ) as $reg => $regexp ) {
56 if ( ! preg_match_all( $regexp, $content, $matches, PREG_SET_ORDER ) ) {
57 continue;
58 }
59
60 foreach ( $matches as $match ) {
61 /*
62 * Hack, but '?' should only ever appear once, and
63 * it should be for the 1st field-value pair in query string,
64 * if it is present
65 * YouTube changed their embed code.
66 * Example of how it is now:
67 * <object width="640" height="385"><param name="movie" value="http://www.youtube.com/v/aP9AaD4tgBY?fs=1&amp;hl=en_US"></param><param name="allowFullScreen" value="true"></param><param name="allowscriptaccess" value="always"></param><embed src="http://www.youtube.com/v/aP9AaD4tgBY?fs=1&amp;hl=en_US" type="application/x-shockwave-flash" allowscriptaccess="always" allowfullscreen="true" width="640" height="385"></embed></object>
68 * As shown at the start of function, previous YouTube didn't '?'
69 * the 1st field-value pair.
70 */
71 if ( in_array( $reg, array( 'ifr_regexp', 'ifr_regexp_ent', 'regexp', 'regexp_ent' ), true ) ) {
72 $params = $match[1];
73
74 if ( in_array( $reg, array( 'ifr_regexp_ent', 'regexp_ent' ), true ) ) {
75 $params = html_entity_decode( $params, ENT_QUOTES | ENT_SUBSTITUTE | ENT_HTML401 );
76 }
77
78 $params = wp_kses_hair( $params, array( 'http' ) );
79
80 $width = isset( $params['width'] ) ? (int) $params['width']['value'] : 0;
81 $height = isset( $params['height'] ) ? (int) $params['height']['value'] : 0;
82 $wh = '';
83
84 if ( $width && $height ) {
85 $wh = "&w=$width&h=$height";
86 }
87
88 $url = esc_url_raw( "https://www.youtube.com/watch?v={$match[3]}{$wh}" );
89 } else {
90 $match[1] = str_replace( '?', '&', $match[1] );
91
92 $url = esc_url_raw( 'https://www.youtube.com/watch?v=' . html_entity_decode( $match[1], ENT_QUOTES | ENT_SUBSTITUTE | ENT_HTML401 ) );
93 }
94
95 $content = str_replace( $match[0], "[youtube $url]", $content );
96
97 /**
98 * Fires before the YouTube embed is transformed into a shortcode.
99 *
100 * @module shortcodes
101 *
102 * @since 1.2.0
103 *
104 * @param string youtube Shortcode name.
105 * @param string $url YouTube video URL.
106 */
107 do_action( 'jetpack_embed_to_shortcode', 'youtube', $url );
108 }
109 }
110
111 return $content;
112 }
113
114 if ( jetpack_shortcodes_should_hook_pre_kses() ) {
115 add_filter( 'pre_kses', 'jetpack_youtube_embed_to_short_code' );
116 }
117
118 /**
119 * Replaces plain-text links to YouTube videos with YouTube embeds.
120 *
121 * @param string $content HTML content.
122 *
123 * @return string The content with embeds instead of URLs
124 */
125 function jetpack_youtube_link( $content ) {
126 return jetpack_preg_replace_callback_outside_tags( '!(?:\n|\A)https?://(?:www\.)?(?:youtube.com/(?:v/|playlist|watch[/\#?])|youtu\.be/)[^\s]+?(?:\n|\Z)!i', 'jetpack_youtube_link_callback', $content, 'youtube.com/' );
127 }
128
129 /**
130 * Callback function for the regex that replaces YouTube URLs with
131 * YouTube embeds.
132 *
133 * @param array $matches An array containing a YouTube URL.
134 */
135 function jetpack_youtube_link_callback( $matches ) {
136 return "\n" . jetpack_youtube_id( $matches[0] ) . "\n";
137 }
138
139 /**
140 * Normalizes a YouTube URL to include a v= parameter and a query string free of encoded ampersands.
141 *
142 * @param string|array $url Youtube URL.
143 * @return string|false The normalized URL or false if input is invalid.
144 */
145 if ( ! function_exists( 'jetpack_youtube_sanitize_url' ) ) :
146 /**
147 * Clean up Youtube URL to match a single format.
148 *
149 * @param string|array $url Youtube URL.
150 */
151 function jetpack_youtube_sanitize_url( $url ) {
152 if ( is_array( $url ) && isset( $url['url'] ) ) {
153 $url = $url['url'];
154 }
155 if ( ! is_string( $url ) ) {
156 return false;
157 }
158
159 $url = trim( $url, ' "' );
160 $url = trim( $url );
161 $url = str_replace( array( 'youtu.be/', '/v/', '#!v=', '&amp;', '&#038;', 'playlist' ), array( 'youtu.be/?v=', '/?v=', '?v=', '&', '&', 'videoseries' ), $url );
162
163 // Replace any extra question marks with ampersands - the result of a URL like "https://www.youtube.com/v/dQw4w9WgXcQ?fs=1&hl=en_US" being passed in.
164 $query_string_start = strpos( $url, '?' );
165
166 if ( false !== $query_string_start ) {
167 $url = substr( $url, 0, $query_string_start + 1 ) . str_replace( '?', '&', substr( $url, $query_string_start + 1 ) );
168 }
169
170 return $url;
171 }
172 endif;
173
174 /**
175 * Converts a YouTube URL into an embedded YouTube video.
176 *
177 * URL can be:
178 * http://www.youtube.com/embed/videoseries?list=PL94269DA08231042B&amp;hl=en_US
179 * http://www.youtube.com/watch#!v=H2Ncxw1xfck
180 * http://www.youtube.com/watch?v=H2Ncxw1xfck
181 * http://www.youtube.com/watch?v=H2Ncxw1xfck&w=320&h=240&fmt=1&rel=0&showsearch=1&hd=0
182 * http://www.youtube.com/v/jF-kELmmvgA
183 * http://www.youtube.com/v/9FhMMmqzbD8?fs=1&hl=en_US
184 * http://youtu.be/Rrohlqeir5E
185 * https://www.youtube.com/watch?v=GJNxoe-iSb4&list=PLAVZ4NFtZX0fE54mDSqNKym-o_rz-8xmk
186 *
187 * @param string $url Youtube URL.
188 */
189 function jetpack_youtube_id( $url ) {
190 $id = jetpack_get_youtube_id( $url );
191
192 if ( ! $id ) {
193 return sprintf( '<!--%s-->', esc_html__( 'YouTube Error: bad URL entered', 'jetpack' ) );
194 }
195
196 $url = jetpack_youtube_sanitize_url( $url );
197 $url = wp_parse_url( $url );
198
199 $thumbnail = "https://i.ytimg.com/vi/$id/hqdefault.jpg";
200 $video_url = add_query_arg( 'v', $id, 'https://www.youtube.com/watch' );
201
202 $args = jetpack_shortcode_youtube_args( $url );
203 if ( empty( $args ) ) {
204 return sprintf( '<!--%s-->', esc_html__( 'YouTube Error: empty URL args', 'jetpack' ) );
205 }
206
207 // Account for URL having both v and list, where jetpack_get_youtube_id() only accounts for one or the other.
208 if ( isset( $args['list'] ) && $args['list'] === $id ) {
209 $id = null;
210 }
211 if ( isset( $args['v'] ) ) {
212 $id = $args['v'];
213 }
214 if ( ! $id && empty( $args['list'] ) ) {
215 return sprintf( '<!--%s-->', esc_html__( 'YouTube Error: missing id and/or list', 'jetpack' ) );
216 }
217
218 list( $w, $h ) = jetpack_shortcode_youtube_dimensions( $args );
219
220 $params = array(
221 'rel' => ( isset( $args['rel'] ) && '0' === $args['rel'] ) ? 0 : 1,
222 'showsearch' => ( isset( $args['showsearch'] ) && '1' === $args['showsearch'] ) ? 1 : 0, // Now deprecated. See https://developers.google.com/youtube/player_parameters#march-29,-2012.
223 'showinfo' => ( isset( $args['showinfo'] ) && '0' === $args['showinfo'] ) ? 0 : 1, // Now obsolete. See https://developers.google.com/youtube/player_parameters#showinfo.
224 'iv_load_policy' => ( isset( $args['iv_load_policy'] ) && '3' === $args['iv_load_policy'] ) ? 3 : 1,
225 'fs' => 1,
226 'hl' => str_replace( '_', '-', get_locale() ),
227 );
228 if ( isset( $args['fmt'] ) && (int) $args['fmt'] ) {
229 $params['fmt'] = (int) $args['fmt']; // Apparently an obsolete parameter. Not referenced on https://developers.google.com/youtube/player_parameters.
230 }
231
232 // The autohide parameter has been deprecated since 2015. See https://developers.google.com/youtube/player_parameters#august-19,-2015.
233 if ( ! isset( $args['autohide'] ) || ( $args['autohide'] < 0 || 2 < $args['autohide'] ) ) {
234 $params['autohide'] = 2;
235 } else {
236 $params['autohide'] = (int) $args['autohide'];
237 }
238
239 $start = 0;
240 if ( isset( $args['start'] ) ) {
241 $start = (int) $args['start'];
242 } elseif ( isset( $args['t'] ) ) {
243 if ( is_numeric( $args['t'] ) ) {
244 $start = (int) $args['t'];
245 } elseif ( is_string( $args['t'] ) ) {
246 $time_pieces = preg_split( '/(?<=\D)(?=\d+)/', $args['t'] );
247
248 foreach ( $time_pieces as $time_piece ) {
249 $int = (int) $time_piece;
250 switch ( substr( $time_piece, - 1 ) ) {
251 case 'h':
252 $start += $int * 3600;
253 break;
254 case 'm':
255 $start += $int * 60;
256 break;
257 case 's':
258 $start += $int;
259 break;
260 }
261 }
262 }
263 }
264 if ( $start ) {
265 $params['start'] = (int) $start;
266 }
267
268 if ( isset( $args['end'] ) && (int) $args['end'] ) {
269 $params['end'] = (int) $args['end'];
270 }
271 if ( isset( $args['hd'] ) && (int) $args['hd'] ) {
272 $params['hd'] = (int) $args['hd']; // Now obsolete per https://developers.google.com/youtube/player_parameters#march-29,-2012.
273 }
274 if ( isset( $args['vq'] ) && in_array( $args['vq'], array( 'hd720', 'hd1080' ), true ) ) {
275 $params['vq'] = $args['vq']; // Note, this appears to be obsolete. Not referenced on https://developers.google.com/youtube/player_parameters.
276 }
277 if ( isset( $args['cc_load_policy'] ) ) {
278 $params['cc_load_policy'] = 1;
279 }
280 if ( isset( $args['cc_lang_pref'] ) ) {
281 $params['cc_lang_pref'] = preg_replace( '/[^_a-z0-9-]/i', '', $args['cc_lang_pref'] );
282 }
283
284 // The wmode parameter appears to be obsolete. Not referenced on https://developers.google.com/youtube/player_parameters.
285 if ( isset( $args['wmode'] ) && in_array( strtolower( $args['wmode'] ), array( 'opaque', 'window', 'transparent' ), true ) ) {
286 $params['wmode'] = $args['wmode'];
287 } else {
288 $params['wmode'] = 'transparent';
289 }
290
291 // The theme parameter is obsolete per https://developers.google.com/youtube/player_parameters#august-19,-2015.
292 if ( isset( $args['theme'] ) && in_array( strtolower( $args['theme'] ), array( 'dark', 'light' ), true ) ) {
293 $params['theme'] = $args['theme'];
294 }
295
296 if ( isset( $args['list'] ) ) {
297 $params['listType'] = 'playlist';
298 $params['list'] = preg_replace( '|[^_a-z0-9-]|i', '', $args['list'] );
299 }
300
301 /**
302 * Allow YouTube videos to start playing automatically.
303 *
304 * @module shortcodes
305 *
306 * @since 2.2.2
307 *
308 * @param bool false Enable autoplay for YouTube videos.
309 */
310 if ( apply_filters( 'jetpack_youtube_allow_autoplay', false ) && isset( $args['autoplay'] ) ) {
311 $params['autoplay'] = (int) $args['autoplay'];
312 }
313
314 $is_amp = class_exists( 'Jetpack_AMP_Support' ) && Jetpack_AMP_Support::is_amp_request();
315
316 if ( $is_amp && $id ) {
317 // Note that <amp-youtube> currently is not well suited for playlists that don't have an individual video selected, hence the $id check above.
318 $placeholder = sprintf(
319 '<a href="%1$s" placeholder><amp-img src="%2$s" alt="%3$s" layout="fill" object-fit="cover"><noscript><img src="%2$s" loading="lazy" decoding="async" alt="%3$s"></noscript></amp-img></a>',
320 esc_url( $video_url ),
321 esc_url( $thumbnail ),
322 esc_attr__( 'YouTube Poster', 'jetpack' ) // Would be preferable to provide YouTube video title, but not available in this non-oEmbed context.
323 );
324
325 $html_attributes = array();
326 foreach ( $params as $param_name => $param_value ) {
327 $html_attributes[] = sprintf(
328 'data-param-%s="%s"',
329 sanitize_key( $param_name ),
330 esc_attr( $param_value )
331 );
332 }
333
334 $html = sprintf(
335 '<amp-youtube data-videoid="%s" %s width="%d" height="%d" layout="responsive">%s</amp-youtube>',
336 esc_attr( $id ),
337 implode( ' ', $html_attributes ), // Note: Escaping done above.
338 esc_attr( $w ),
339 esc_attr( $h ),
340 $placeholder
341 );
342 } else {
343 // In AMP, the AMP_Iframe_Sanitizer will convert into <amp-iframe> as required.
344 $src = 'https://www.youtube.com/embed';
345 if ( $id ) {
346 $src .= "/$id";
347 }
348 $src = add_query_arg(
349 array_merge(
350 array( 'version' => 3 ),
351 $params
352 ),
353 $src
354 );
355
356 $layout = $is_amp ? 'layout="responsive" ' : '';
357
358 $html = sprintf(
359 '<iframe class="youtube-player" width="%s" height="%s" %ssrc="%s" allowfullscreen="true" style="border:0;" sandbox="allow-scripts allow-same-origin allow-popups allow-presentation allow-popups-to-escape-sandbox"></iframe>',
360 esc_attr( $w ),
361 esc_attr( $h ),
362 $layout,
363 esc_url( $src )
364 );
365 }
366
367 // Let's do some alignment wonder in a span, unless we're producing a feed.
368 if ( ! is_feed() ) {
369 $alignmentcss = 'text-align:center;';
370 if ( isset( $args['align'] ) ) {
371 switch ( $args['align'] ) {
372 case 'left':
373 $alignmentcss = "float:left; width:{$w}px; height:{$h}px; margin-right:10px; margin-bottom: 10px;";
374 break;
375 case 'right':
376 $alignmentcss = "float:right; width:{$w}px; height:{$h}px; margin-left:10px; margin-bottom: 10px;";
377 break;
378 }
379 }
380
381 $html = sprintf(
382 '<span class="embed-youtube" style="%s display: block;">%s</span>',
383 esc_attr( $alignmentcss ),
384 $html
385 );
386
387 }
388
389 /**
390 * Format output for Calypso Reader/Notifications/Comments
391 */
392 if ( defined( 'IS_WPCOM' ) && IS_WPCOM ) {
393 require_once WP_CONTENT_DIR . '/lib/display-context.php';
394 $context = A8C\Display_Context\get_current_context();
395 if ( A8C\Display_Context\NOTIFICATIONS === $context ) {
396 return sprintf(
397 '<a href="%1$s" target="_blank" rel="noopener noreferrer"><img src="%2$s" alt="%3$s" /></a>',
398 esc_url( $video_url ),
399 esc_url( $thumbnail ),
400 esc_html__( 'YouTube video', 'jetpack' )
401 );
402 }
403 }
404
405 /**
406 * Filter the YouTube video HTML output.
407 *
408 * @module shortcodes
409 *
410 * @since 1.2.3
411 *
412 * @param string $html YouTube video HTML output.
413 */
414 $html = apply_filters( 'video_embed_html', $html );
415
416 return $html;
417 }
418
419 /**
420 * Gets the args present in the YouTube shortcode URL.
421 *
422 * @since 8.0.0
423 *
424 * @param array $url The parsed URL of the shortcode.
425 *
426 * @return array|false The query args of the URL, or false.
427 */
428 function jetpack_shortcode_youtube_args( $url ) {
429 $qargs = array();
430 if ( ! empty( $url['query'] ) ) {
431 wp_parse_str( $url['query'], $qargs );
432 } else {
433 return false;
434 }
435
436 $fargs = array();
437 if ( ! empty( $url['fragment'] ) ) {
438 wp_parse_str( $url['fragment'], $fargs );
439 }
440
441 return array_merge( $fargs, $qargs );
442 }
443
444 /**
445 * Display the Youtube shortcode.
446 *
447 * @param array $atts Shortcode attributes.
448 *
449 * @return string The rendered shortcode.
450 */
451 function jetpack_youtube_shortcode( $atts ) {
452 $url = ( isset( $atts[0] ) ) ? ltrim( $atts[0], '=' ) : shortcode_new_to_old_params( $atts );
453 return jetpack_youtube_id( $url );
454 }
455 add_shortcode( 'youtube', 'jetpack_youtube_shortcode' );
456
457 /**
458 * Gets the dimensions of the [youtube] shortcode.
459 *
460 * Calculates the width and height, taking $content_width into consideration.
461 *
462 * @since 8.0.0
463 *
464 * @param array $query_args The query args of the URL.
465 *
466 * @return array The width and height of the shortcode.
467 */
468 function jetpack_shortcode_youtube_dimensions( $query_args ) {
469 $h = null;
470 global $content_width;
471
472 $input_w = ( isset( $query_args['w'] ) && (int) $query_args['w'] ) ? (int) $query_args['w'] : 0;
473 $input_h = ( isset( $query_args['h'] ) && (int) $query_args['h'] ) ? (int) $query_args['h'] : 0;
474
475 // If we have $content_width, use it.
476 if ( ! empty( $content_width ) ) {
477 $default_width = (int) $content_width;
478 } else {
479 // Otherwise get default width from the old, now deprecated embed_size_w option.
480 $default_width = (int) get_option( 'embed_size_w' );
481 }
482
483 // If we don't know those 2 values use a hardcoded width.
484 if ( empty( $default_width ) ) {
485 $default_width = 640;
486 }
487
488 if ( $input_w > 0 && $input_h > 0 ) {
489 $w = $input_w;
490 $h = $input_h;
491 } elseif ( 0 === $input_w && 0 === $input_h ) {
492 if ( isset( $query_args['fmt'] ) && (int) $query_args['fmt'] ) {
493 $w = ( ! empty( $content_width ) ? min( $content_width, 480 ) : 480 );
494 } else {
495 $w = ( ! empty( $content_width ) ? min( $content_width, $default_width ) : $default_width );
496 $h = ceil( ( $w / 16 ) * 9 );
497 }
498 } elseif ( $input_w > 0 ) {
499 $w = $input_w;
500 $h = ceil( ( $w / 16 ) * 9 );
501 } elseif ( isset( $query_args['fmt'] ) && (int) $query_args['fmt'] ) {
502 $w = ( ! empty( $content_width ) ? min( $content_width, 480 ) : 480 );
503 } else {
504 $w = ( ! empty( $content_width ) ? min( $content_width, $default_width ) : $default_width );
505 $h = $input_h;
506 }
507
508 /**
509 * Filter the YouTube player width.
510 *
511 * @module shortcodes
512 *
513 * @since 1.1.0
514 *
515 * @param int $w Width of the YouTube player in pixels.
516 */
517 $w = (int) apply_filters( 'youtube_width', $w );
518
519 /**
520 * Filter the YouTube player height.
521 *
522 * @module shortcodes
523 *
524 * @since 1.1.0
525 *
526 * @param int $h Height of the YouTube player in pixels.
527 */
528 $h = (int) apply_filters( 'youtube_height', $h );
529
530 return array( $w, $h );
531 }
532
533 /**
534 * For bare URLs on their own line of the form
535 * http://www.youtube.com/v/9FhMMmqzbD8?fs=1&hl=en_US
536 *
537 * @param array $matches Regex partial matches against the URL passed.
538 * @param array $attr Attributes received in embed response.
539 * @param string $url Requested URL to be embedded.
540 */
541 function wpcom_youtube_embed_crazy_url( $matches, $attr, $url ) {
542 return jetpack_youtube_id( $url );
543 }
544
545 /**
546 * Get the regex for Youtube URLs.
547 */
548 function wpcom_youtube_get_regex() {
549 return '#https?://(?:www\.)?(?:youtube.com/(?:v/|playlist|watch[/\#?])|youtu\.be/).*#i';
550 }
551
552 /**
553 * Add a new handler to automatically transform custom Youtube URLs (like playlists) into embeds.
554 */
555 function wpcom_youtube_embed_crazy_url_init() {
556 if ( ! defined( 'REST_API_REQUEST' ) ) {
557 return;
558 }
559
560 // Register the custom handler to provide the better support for the private video.
561 wp_embed_register_handler( 'wpcom_youtube_embed_crazy_url', wpcom_youtube_get_regex(), 'wpcom_youtube_embed_crazy_url' );
562 }
563 add_action( 'init', 'wpcom_youtube_embed_crazy_url_init' );
564
565 /**
566 * Filters the oEmbed result before any HTTP requests are made for YouTube.
567 *
568 * @since 13.9
569 *
570 * @param null|string $result The UNSANITIZED (and potentially unsafe) HTML that should be used to embed. Default null.
571 * @param string $url The URL that should be inspected for discovery `<link>` tags.
572 * @param array $args oEmbed remote get arguments.
573 * @return null|string The UNSANITIZED (and potentially unsafe) HTML that should be used to embed.
574 * Null if the URL does not belong to the current site.
575 */
576 function wpcom_youtube_filter_pre_oembed_result( $result, $url, $args ) {
577 // Return early if it's not a YouTube URL.
578 if ( ! preg_match( wpcom_youtube_get_regex(), $url, $matches ) ) {
579 return $result;
580 }
581
582 // Try to get the oembed data by the Core's approach.
583 $wp_oembed = _wp_oembed_get_object();
584 $data = $wp_oembed->get_data( $url, $args );
585 if ( $data ) {
586 /** This filter is documented in wp-includes/class-wp-oembed.php */
587 return apply_filters( 'oembed_result', $wp_oembed->data2html( $data, $url ), $url, $args );
588 }
589
590 // Fallback to the custom handler if the oembed result is not found, especially for the private video.
591 return jetpack_youtube_id( $url );
592 }
593 add_filter( 'pre_oembed_result', 'wpcom_youtube_filter_pre_oembed_result', 10, 3 );
594
595 /**
596 * Remove the ending question mark from the video id of the YouTube URL.
597 *
598 * Example: https://www.youtube.com/watch?v=AVAWwXeOyyQ?
599 *
600 * @since 13.9
601 *
602 * @param string $provider URL of the oEmbed provider.
603 * @param string $url URL of the content to be embedded.
604 *
605 * @return string
606 */
607 function wpcom_youtube_oembed_fetch_url( $provider, $url ) {
608 if ( ! wp_startswith( $provider, 'https://www.youtube.com/oembed' ) ) {
609 return $provider;
610 }
611
612 $parsed = wp_parse_url( $url );
613 if ( ! isset( $parsed['query'] ) ) {
614 return $provider;
615 }
616
617 $query_vars = array();
618 wp_parse_str( $parsed['query'], $query_vars );
619 if ( isset( $query_vars['v'] ) && wp_endswith( $query_vars['v'], '?' ) ) {
620 $url = remove_query_arg( array( 'v' ), $url );
621 $url = add_query_arg( 'v', preg_replace( '/\?$/', '', $query_vars['v'] ), $url );
622 }
623
624 $provider = remove_query_arg( array( 'url' ), $provider );
625 $provider = add_query_arg( 'url', rawurlencode( $url ), $provider );
626
627 return $provider;
628 }
629 add_filter( 'oembed_fetch_url', 'wpcom_youtube_oembed_fetch_url', 10, 2 );
630
631 if (
632 ! is_admin()
633 &&
634 /**
635 * Allow oEmbeds in Jetpack's Comment form.
636 *
637 * @module shortcodes
638 *
639 * @since 2.8.0
640 *
641 * @param int $allow_oembed Option to automatically embed all plain text URLs.
642 */
643 apply_filters( 'jetpack_comments_allow_oembed', true )
644 // No need for this on WordPress.com, this is done for multiple shortcodes at a time there.
645 && ( ! defined( 'IS_WPCOM' ) || ! IS_WPCOM )
646 ) {
647 /*
648 * We attach wp_kses_post to comment_text in default-filters.php with priority of 10 anyway,
649 * so the iframe gets filtered out.
650 * Higher priority because we need it before auto-link and autop get to it.
651 */
652 add_filter( 'comment_text', 'jetpack_youtube_link', 1 );
653 }
654
655 /**
656 * Core changes to do_shortcode (https://core.trac.wordpress.org/changeset/34747) broke "improper" shortcodes
657 * with the format [shortcode=http://url.com].
658 *
659 * This removes the "=" from the shortcode so it can be parsed.
660 *
661 * @see https://github.com/Automattic/jetpack/issues/3121
662 *
663 * @param string $content HTML content.
664 */
665 function jetpack_fix_youtube_shortcode_display_filter( $content ) {
666 if ( strpos( $content, '[youtube=' ) !== false ) {
667 $content = preg_replace( '@\[youtube=(.*?)\]@', '[youtube $1]', $content );
668 }
669
670 return $content;
671 }
672 add_filter( 'the_content', 'jetpack_fix_youtube_shortcode_display_filter', 7 );
673