PluginProbe ʕ •ᴥ•ʔ
Jetpack – WP Security, Backup, Speed, & Growth / 13.7.2
Jetpack – WP Security, Backup, Speed, & Growth v13.7.2
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 / videopress / class.videopress-player.php
jetpack / modules / videopress Last commit date
css 2 years ago js 3 years ago class-videopress-attachment-metadata.php 2 years ago class.jetpack-videopress.php 2 years ago class.videopress-cli.php 2 years ago class.videopress-edit-attachment.php 3 years ago class.videopress-gutenberg.php 3 years ago class.videopress-options.php 2 years ago class.videopress-player.php 2 years ago class.videopress-scheduler.php 4 years ago class.videopress-video.php 4 years ago class.videopress-xmlrpc.php 2 years ago editor-media-view.php 2 years ago shortcode.php 2 years ago videopress-admin-rtl.css 4 years ago videopress-admin-rtl.min.css 2 years ago videopress-admin.css 4 years ago videopress-admin.min.css 2 years ago
class.videopress-player.php
926 lines
1 <?php // phpcs:ignore WordPress.Files.FileName.InvalidClassFileName
2 use Automattic\Jetpack\VideoPress\Jwt_Token_Bridge;
3
4 /**
5 * VideoPress playback module markup generator.
6 *
7 * @since 1.3
8 */
9 class VideoPress_Player {
10 /**
11 * Video data for the requested guid and maximum width
12 *
13 * @since 1.3
14 * @var VideoPress_Video
15 */
16 protected $video;
17
18 /**
19 * DOM identifier of the video container
20 *
21 * @var string
22 * @since 1.3
23 */
24 protected $video_container_id;
25
26 /**
27 * DOM identifier of the video element (video, object, embed)
28 *
29 * @var string
30 * @since 1.3
31 */
32 protected $video_id;
33
34 /**
35 * Array of playback options: force_flash or freedom
36 *
37 * @var array
38 * @since 1.3
39 */
40 protected $options;
41
42 /**
43 * Array of video GUIDs shown and their counts,
44 * moved from the old VideoPress class.
45 *
46 * @var array
47 */
48 public static $shown = array();
49
50 /**
51 * Initiate a player object based on shortcode values and possible blog-level option overrides
52 *
53 * @since 1.3
54 * @param string $guid VideoPress unique identifier.
55 * @param int $maxwidth Maximum desired width of the video player if specified.
56 * @param array $options Player customizations.
57 */
58 public function __construct( $guid, $maxwidth = 0, $options = array() ) {
59 if ( empty( self::$shown[ $guid ] ) ) {
60 self::$shown[ $guid ] = 0;
61 }
62
63 ++self::$shown[ $guid ];
64
65 $this->video_container_id = 'v-' . $guid . '-' . self::$shown[ $guid ];
66 $this->video_id = $this->video_container_id . '-video';
67
68 if ( is_array( $options ) ) {
69 $this->options = $options;
70 } else {
71 $this->options = array();
72 }
73
74 // set up the video
75 $cache_key = null;
76
77 // disable cache in debug mode
78 if ( defined( 'WP_DEBUG' ) && WP_DEBUG === true ) {
79 $cached_video = null;
80 } else {
81 $cache_key_pieces = array( 'video' );
82
83 if ( is_multisite() && is_subdomain_install() ) {
84 $cache_key_pieces[] = get_current_blog_id();
85 }
86
87 $cache_key_pieces[] = $guid;
88 if ( $maxwidth > 0 ) {
89 $cache_key_pieces[] = $maxwidth;
90 }
91 if ( is_ssl() ) {
92 $cache_key_pieces[] = 'ssl';
93 }
94 $cache_key = implode( '-', $cache_key_pieces );
95 unset( $cache_key_pieces );
96 $cached_video = wp_cache_get( $cache_key, 'video' );
97 }
98 if ( empty( $cached_video ) ) {
99 $video = new VideoPress_Video( $guid, $maxwidth );
100 if ( empty( $video ) ) {
101 return;
102 } elseif ( isset( $video->error ) ) {
103 $this->video = $video->error;
104 return;
105 } elseif ( is_wp_error( $video ) ) {
106 $this->video = $video;
107 return;
108 }
109
110 $this->video = $video;
111 unset( $video );
112
113 if ( ! defined( 'WP_DEBUG' ) || WP_DEBUG !== true ) {
114 $expire = 3600;
115 if ( isset( $this->video->expires ) && is_int( $this->video->expires ) ) {
116 $expires_diff = time() - $this->video->expires;
117 if ( $expires_diff > 0 && $expires_diff < 86400 ) { // allowed range: 1 second to 1 day
118 $expire = $expires_diff;
119 }
120 unset( $expires_diff );
121 }
122
123 wp_cache_set( $cache_key, serialize( $this->video ), 'video', $expire ); // phpcs:ignore WordPress.PHP.DiscouragedPHPFunctions.serialize_serialize
124 unset( $expire );
125 }
126 } else {
127 $this->video = unserialize( $cached_video ); // phpcs:ignore WordPress.PHP.DiscouragedPHPFunctions.serialize_unserialize -- Make sure to unserialize as VideoPress_Video class.
128 }
129 unset( $cache_key );
130 unset( $cached_video );
131 }
132
133 /**
134 * Wrap output in a VideoPress player container.
135 *
136 * @since 1.3
137 * @param string $content HTML string.
138 * @return string HTML string or blank string if nothing to wrap.
139 */
140 private function html_wrapper( $content ) {
141 if ( empty( $content ) ) {
142 return '';
143 } else {
144 return '<div id="' . esc_attr( $this->video_container_id ) . '" class="video-player">' . $content . '</div>';
145 }
146 }
147
148 /**
149 * Output content suitable for a feed reader displaying RSS or Atom feeds
150 * We do not display error messages in the feed view due to caching concerns.
151 * Flash content presented using <embed> markup for feed reader compatibility.
152 *
153 * @since 1.3
154 * @return string HTML string or empty string if error
155 */
156 public function as_xml() {
157 if ( empty( $this->video ) || is_wp_error( $this->video ) ) {
158 return '';
159 }
160
161 if ( isset( $this->options['force_flash'] ) && true === $this->options['force_flash'] ) {
162 $content = $this->flash_embed();
163
164 } else {
165 $content = $this->html5_static();
166 }
167
168 return $this->html_wrapper( $content );
169 }
170
171 /**
172 * Video player markup for best matching the current request and publisher options
173 *
174 * @since 1.3
175 * @return string HTML markup string or empty string if no video property found
176 */
177 public function as_html() {
178 if ( empty( $this->video ) ) {
179 $content = '';
180
181 } elseif ( is_wp_error( $this->video ) ) {
182 $content = $this->error_message( $this->video );
183
184 } elseif ( isset( $this->options['force_flash'] ) && true === $this->options['force_flash'] ) {
185 $content = $this->flash_object();
186
187 } elseif ( isset( $this->video->restricted_embed ) && true === $this->video->restricted_embed ) {
188
189 if ( $this->options['forcestatic'] ) {
190 $content = $this->flash_object();
191
192 } else {
193 $content = $this->html5_dynamic();
194 }
195 } elseif ( isset( $this->options['freedom'] ) && true === $this->options['freedom'] ) {
196 $content = $this->html5_static();
197
198 } else {
199 $content = $this->html5_dynamic();
200 }
201
202 return $this->html_wrapper( $content );
203 }
204
205 /**
206 * Display an error message to users capable of doing something about the error
207 *
208 * @since 1.3
209 * @uses current_user_can() to test if current user has edit_posts capability.
210 * @param WP_Error $error WordPress error.
211 * @return string HTML string
212 */
213 private function error_message( $error ) {
214 if ( ! current_user_can( 'edit_posts' ) || empty( $error ) ) {
215 return '';
216 }
217
218 $html = '<div class="videopress-error" style="background-color:rgb(255,0,0);color:rgb(255,255,255);font-family:font-family:\'Helvetica Neue\',Arial,Helvetica,\'Nimbus Sans L\',sans-serif;font-size:140%;min-height:10em;padding-top:1.5em;padding-bottom:1.5em">';
219 /* translators: %s is 'VideoPress' */
220 $html .= '<h1 style="font-size:180%;font-style:bold;line-height:130%;text-decoration:underline">' . esc_html( sprintf( __( '%s Error', 'jetpack' ), 'VideoPress' ) ) . '</h1>';
221 foreach ( $error->get_error_messages() as $message ) {
222 $html .= $message;
223 }
224 $html .= '</div>';
225 return $html;
226 }
227
228 /**
229 * Rating agencies and industry associations require a potential viewer verify their age before a video or its poster frame are displayed.
230 * Content rated for audiences 17 years of age or older requires such verification across multiple rating agencies and industry associations
231 *
232 * @since 1.3
233 * @return bool true if video requires the viewer verify they are 17 years of age or older
234 */
235 private function age_gate_required() {
236 if ( isset( $this->video->age_rating ) && $this->video->age_rating >= 17 ) {
237 return true;
238 } else {
239 return false;
240 }
241 }
242
243 /**
244 * Select a date of birth using HTML form elements.
245 *
246 * @since 1.5
247 * @return string HTML markup
248 */
249 private function html_age_gate() {
250 global $wp_locale;
251 $text_align = 'left';
252 if ( $this->video->text_direction === 'rtl' ) {
253 $text_align = 'right';
254 }
255
256 $html = '<div class="videopress-age-gate" style="margin:0 60px">';
257 $html .= '<p class="instructions" style="color:rgb(255, 255, 255);font-size:21px;padding-top:60px;padding-bottom:20px;text-align:' . $text_align . '">' . esc_html( __( 'This video is intended for mature audiences.', 'jetpack' ) ) . '<br />' . esc_html( __( 'Please verify your birthday.', 'jetpack' ) ) . '</p>';
258 $html .= '<fieldset id="birthday" style="border:0 none;text-align:' . $text_align . ';padding:0;">';
259 $inputs_style = 'border:1px solid #444;margin-';
260 if ( $this->video->text_direction === 'rtl' ) {
261 $inputs_style .= 'left';
262 } else {
263 $inputs_style .= 'right';
264 }
265 $inputs_style .= ':10px;background-color:rgb(0, 0, 0);font-size:14px;color:rgb(255,255,255);padding:4px 6px;line-height: 2em;vertical-align: middle';
266
267 /**
268 * Display a list of months in the Gregorian calendar.
269 * Set values to 0-based to match JavaScript Date.
270 *
271 * @link https://developer.mozilla.org/en/JavaScript/Reference/global_objects/date Mozilla JavaScript Reference: Date
272 */
273 $html .= '<select name="month" style="' . $inputs_style . '">';
274
275 for ( $i = 0; $i < 12; $i++ ) {
276 $html .= '<option value="' . esc_attr( $i ) . '">' . esc_html( $wp_locale->get_month( $i + 1 ) ) . '</option>';
277 }
278 $html .= '</select>';
279
280 /**
281 * Todo: numdays variance by month.
282 */
283 $html .= '<select name="day" style="' . $inputs_style . '">';
284 for ( $i = 1; $i < 32; $i++ ) {
285 $html .= '<option>' . $i . '</option>';
286 }
287 $html .= '</select>';
288
289 /**
290 * Current record for human life is 122. Go back 130 years and no one is left out.
291 * Don't ask infants younger than 2 for their birthday
292 * Default to 13
293 */
294 $html .= '<select name="year" style="' . $inputs_style . '">';
295 $start_year = gmdate( 'Y' ) - 2;
296 $default_year = $start_year - 11;
297 $end_year = $start_year - 128;
298 for ( $year = $start_year; $year > $end_year; $year-- ) {
299 $html .= '<option';
300 if ( $year === $default_year ) {
301 $html .= ' selected="selected"';
302 }
303 $html .= '>' . $year . '</option>';
304 }
305 unset( $start_year );
306 unset( $default_year );
307 unset( $end_year );
308 $html .= '</select>';
309
310 $html .= '<input type="submit" value="' . __( 'Submit', 'jetpack' ) . '" style="cursor:pointer;border-radius: 1em;border:1px solid #333;background-color:#333;background:-webkit-gradient( linear, left top, left bottom, color-stop(0.0, #444), color-stop(1, #111) );background:-moz-linear-gradient(center top, #444 0%, #111 100%);font-size:13px;padding:4px 10px 5px;line-height:1em;vertical-align:top;color:white;text-decoration:none;margin:0" />';
311
312 $html .= '</fieldset>';
313 $html .= '<p style="padding-top:20px;padding-bottom:60px;text-align:' . $text_align . ';"><a rel="nofollow noopener noreferrer" href="https://videopress.com/" target="_blank" style="color:rgb(128,128,128);text-decoration:underline;font-size:15px">' . __( 'More information', 'jetpack' ) . '</a></p>';
314
315 $html .= '</div>';
316 return $html;
317 }
318
319 /**
320 * Return HTML5 video static markup for the given video parameters.
321 * Use default browser player controls.
322 * No Flash fallback.
323 *
324 * @since 1.2
325 * @link https://html.spec.whatwg.org/multipage/media.html#the-video-element HTML5 video
326 * @return string HTML5 video element and children
327 */
328 private function html5_static() {
329 wp_enqueue_script( 'videopress' );
330 $thumbnail = esc_url( $this->video->poster_frame_uri );
331 $html = "<video id=\"{$this->video_id}\" width=\"{$this->video->calculated_width}\" height=\"{$this->video->calculated_height}\" poster=\"$thumbnail\" controls=\"true\"";
332
333 $preload = 'metadata';
334 if ( isset( $this->options['preloadContent'] ) && videopress_is_valid_preload( $this->options['preloadContent'] ) ) {
335 $preload = $this->options['preloadContent'];
336 }
337
338 if ( isset( $this->options['autoplay'] ) && $this->options['autoplay'] === true ) {
339 $html .= ' autoplay="true"';
340 } else {
341 $html .= ' preload="' . esc_attr( $preload ) . '"';
342 }
343 if ( isset( $this->video->text_direction ) ) {
344 $html .= ' dir="' . esc_attr( $this->video->text_direction ) . '"';
345 }
346 if ( isset( $this->video->language ) ) {
347 $html .= ' lang="' . esc_attr( $this->video->language ) . '"';
348 }
349 $html .= '>';
350 if (
351 ( ! isset( $this->options['freedom'] ) || $this->options['freedom'] === false )
352 && isset( $this->video->videos->mp4 )
353 ) {
354 $mp4 = $this->video->videos->mp4->url;
355 if ( ! empty( $mp4 ) ) {
356 $html .= '<source src="' . esc_url( $mp4 ) . '" type="video/mp4; codecs=&quot;' . esc_attr( $this->video->videos->mp4->codecs ) . '&quot;" />';
357 }
358 unset( $mp4 );
359 }
360
361 if ( isset( $this->video->videos->ogv ) ) {
362 $ogg = $this->video->videos->ogv->url;
363 if ( ! empty( $ogg ) ) {
364 $html .= '<source src="' . esc_url( $ogg ) . '" type="video/ogg; codecs=&quot;' . esc_attr( $this->video->videos->ogv->codecs ) . '&quot;" />';
365 }
366
367 unset( $ogg );
368 }
369
370 $html .= '<div><img alt="';
371 if ( isset( $this->video->title ) ) {
372 $html .= esc_attr( $this->video->title );
373 }
374 $html .= '" src="' . $thumbnail . '" width="' . $this->video->calculated_width . '" height="' . $this->video->calculated_height . '" /></div>';
375 if ( isset( $this->options['freedom'] ) && $this->options['freedom'] === true ) {
376 /* translators: %s url to the gnu.org website */
377 $html .= '<p class="robots-nocontent">' . sprintf( __( 'You do not have sufficient <a rel="nofollow noopener noreferrer" href="%s" target="_blank">freedom levels</a> to view this video. Support free software and upgrade.', 'jetpack' ), 'https://www.gnu.org/philosophy/free-sw.html' ) . '</p>';
378 } elseif ( isset( $this->video->title ) ) {
379 $html .= '<p>' . esc_html( $this->video->title ) . '</p>';
380 }
381 $html .= '</video>';
382 return $html;
383 }
384
385 /**
386 * Click to play dynamic HTML5-capable player.
387 * The player displays a video preview section including poster frame,
388 * video title, play button and watermark on the original page load
389 * and calculates the playback capabilities of the browser. The video player
390 * is loaded when the visitor clicks on the video preview area.
391 * If Flash Player 10 or above is available the browser will display
392 * the Flash version of the video. If HTML5 video appears to be supported
393 * and the browser may be capable of MP4 (H.264, AAC) or OGV (Theora, Vorbis)
394 * playback the browser will display its native HTML5 player.
395 *
396 * @since 1.5
397 * @return string HTML markup
398 */
399 private function html5_dynamic() {
400
401 /**
402 * Filter the VideoPress legacy player feature
403 *
404 * This filter allows you to control whether the legacy VideoPress player should be used
405 * instead of the improved one.
406 *
407 * @module videopress
408 *
409 * @since 3.7.0
410 *
411 * @param boolean $videopress_use_legacy_player
412 */
413 if ( ! apply_filters( 'jetpack_videopress_use_legacy_player', false ) ) {
414 return $this->html5_dynamic_next();
415 }
416
417 wp_enqueue_script( 'videopress' );
418 $video_placeholder_id = $this->video_container_id . '-placeholder';
419 $age_gate_required = $this->age_gate_required();
420 $width = absint( $this->video->calculated_width );
421 $height = absint( $this->video->calculated_height );
422
423 $html = '<div id="' . $video_placeholder_id . '" class="videopress-placeholder" style="';
424 if ( $age_gate_required ) {
425 $html .= "min-width:{$width}px;min-height:{$height}px";
426 } else {
427 $html .= "width:{$width}px;height:{$height}px";
428 }
429 $html .= ';display:none;cursor:pointer !important;position:relative;';
430 if ( isset( $this->video->skin ) && isset( $this->video->skin->background_color ) ) {
431 $html .= 'background-color:' . esc_attr( $this->video->skin->background_color ) . ';';
432 }
433 $html .= 'font-family: \'Helvetica Neue\',Arial,Helvetica,\'Nimbus Sans L\',sans-serif;font-weight:bold;font-size:18px">' . PHP_EOL;
434
435 /**
436 * Do not display a poster frame, title, or any other content hints for mature content.
437 */
438 if ( ! $age_gate_required ) {
439 if ( ! empty( $this->video->title ) ) {
440 $html .= '<div class="videopress-title" style="display:inline;position:absolute;margin:20px 20px 0 20px;padding:4px 8px;vertical-align:top;text-align:';
441 if ( $this->video->text_direction === 'rtl' ) {
442 $html .= 'right" dir="rtl"';
443 } else {
444 $html .= 'left" dir="ltr"';
445 }
446 if ( isset( $this->video->language ) ) {
447 $html .= ' lang="' . esc_attr( $this->video->language ) . '"';
448 }
449 $html .= '><span style="padding:3px 0;line-height:1.5em;';
450 if ( isset( $this->video->skin ) && isset( $this->video->skin->background_color ) ) {
451 $html .= 'background-color:';
452 if ( $this->video->skin->background_color === 'rgb(0,0,0)' ) {
453 $html .= 'rgba(0,0,0,0.8)';
454 } else {
455 $html .= esc_attr( $this->video->skin->background_color );
456 }
457 $html .= ';';
458 }
459 $html .= 'color:rgb(255,255,255)">' . esc_html( $this->video->title ) . '</span></div>';
460 }
461 $html .= '<img class="videopress-poster" alt="';
462 if ( ! empty( $this->video->title ) ) {
463 /* translators: %s is the video title */
464 $html .= esc_attr( $this->video->title ) . '" title="' . esc_attr( sprintf( _x( 'Watch: %s', 'watch a video title', 'jetpack' ), $this->video->title ) );
465 }
466 $html .= '" src="' . esc_url( $this->video->poster_frame_uri, array( 'http', 'https' ) ) . '" width="' . $width . '" height="' . $height . '" />' . PHP_EOL;
467
468 // style a play button hovered over the poster frame
469 $html .= '<div class="play-button"><span style="z-index:2;display:block;position:absolute;top:50%;left:50%;text-align:center;vertical-align:middle;color:rgb(255,255,255);opacity:0.9;margin:0 0 0 -0.45em;padding:0;line-height:0;font-size:500%;text-shadow:0 0 40px rgba(0,0,0,0.5)">&#9654;</span></div>' . PHP_EOL;
470
471 // watermark
472 if ( isset( $this->video->skin ) && isset( $this->video->skin->watermark ) ) {
473 $html .= '<div style="position:relative;margin-top:-40px;height:25px;margin-bottom:35px;';
474 if ( $this->video->text_direction === 'rtl' ) {
475 $html .= 'margin-left:20px;text-align:left;';
476 } else {
477 $html .= 'margin-right:20px;text-align:right;';
478 }
479 $html .= 'vertical-align:bottom;z-index:3">';
480 $html .= '<img alt="" src="' . esc_url( $this->video->skin->watermark, array( 'http', 'https' ) ) . '" width="90" height="13" style="background-color:transparent;background-image:none;background-repeat:no-repeat;border:none;margin:0;padding:0"/>';
481 $html .= '</div>' . PHP_EOL;
482 }
483 }
484
485 $data = array(
486 'blog' => absint( $this->video->blog_id ),
487 'post' => absint( $this->video->post_id ),
488 'duration' => absint( $this->video->duration ),
489 'poster' => esc_url_raw( $this->video->poster_frame_uri, array( 'http', 'https' ) ),
490 'hd' => (bool) $this->options['hd'],
491 );
492 if ( isset( $this->video->videos ) ) {
493 if ( isset( $this->video->videos->mp4 ) && isset( $this->video->videos->mp4->url ) ) {
494 $data['mp4'] = array(
495 'size' => $this->video->videos->mp4->format,
496 'uri' => esc_url_raw( $this->video->videos->mp4->url, array( 'http', 'https' ) ),
497 );
498 }
499 if ( isset( $this->video->videos->ogv ) && isset( $this->video->videos->ogv->url ) ) {
500 $data['ogv'] = array(
501 'size' => 'std',
502 'uri' => esc_url_raw( $this->video->videos->ogv->url, array( 'http', 'https' ) ),
503 );
504 }
505 }
506 $locale = array( 'dir' => $this->video->text_direction );
507 if ( isset( $this->video->language ) ) {
508 $locale['lang'] = $this->video->language;
509 }
510 $data['locale'] = $locale;
511 unset( $locale );
512
513 $guid = $this->video->guid;
514 $guid_js = wp_json_encode( $guid );
515 $html .= '<script type="text/javascript">' . PHP_EOL;
516 $html .= 'jQuery(document).ready(function() {';
517
518 $html .= 'if ( !jQuery.VideoPress.data[' . wp_json_encode( $guid ) . '] ) { jQuery.VideoPress.data[' . wp_json_encode( $guid ) . '] = new Array(); }' . PHP_EOL;
519 $html .= 'jQuery.VideoPress.data[' . wp_json_encode( $guid ) . '][' . self::$shown[ $guid ] . ']=' . wp_json_encode( $data ) . ';' . PHP_EOL;
520 unset( $data );
521
522 $jq_container = wp_json_encode( '#' . $this->video_container_id );
523 $jq_placeholder = wp_json_encode( '#' . $video_placeholder_id );
524 $player_config = "{width:{$width},height:{$height},";
525 if ( isset( $this->options['freedom'] ) && $this->options['freedom'] === true ) {
526 $player_config .= 'freedom:"true",';
527 }
528 $player_config .= 'container:jQuery(' . $jq_container . ')}';
529
530 $html .= "jQuery({$jq_placeholder}).show(0,function(){jQuery.VideoPress.analytics.impression({$guid_js})});" . PHP_EOL;
531
532 if ( $age_gate_required ) {
533 $html .= 'if ( jQuery.VideoPress.support.flash() ) {' . PHP_EOL;
534 /**
535 * Insert alternative content for Flash players.
536 *
537 * @link https://github.com/swfobject/swfobject/wiki/SWFObject-API#swfobjectembedswfswfurlstr-replaceelemidstr-widthstr-heightstr-swfversionstr-xiswfurlstr-flashvarsobj-parobj-attobj-callbackfn
538 */
539 $html .= 'swfobject.embedSWF(' . implode(
540 ',',
541 array(
542 'jQuery.VideoPress.video.flash.player_uri',
543 wp_json_encode( $this->video_container_id ),
544 wp_json_encode( $width ),
545 wp_json_encode( $height ),
546 'jQuery.VideoPress.video.flash.min_version',
547 'jQuery.VideoPress.video.flash.expressinstall', // attempt to upgrade the Flash player if less than min_version. requires a 310x137 container or larger but we will always try to include
548 '{guid:' . $guid_js . '}', // FlashVars
549 'jQuery.VideoPress.video.flash.params',
550 'null', // no attributes
551 'jQuery.VideoPress.video.flash.embedCallback', // error fallback
552 )
553 ) . ');';
554 $html .= '} else {' . PHP_EOL;
555 $html .= "if ( jQuery.VideoPress.video.prepare({$guid_js},{$player_config}," . self::$shown[ $guid ] . ') ) {' . PHP_EOL;
556 $html .= 'if ( jQuery(' . $jq_container . ').data( "player" ) === "flash" ){jQuery.VideoPress.video.play(jQuery(' . wp_json_encode( '#' . $this->video_container_id ) . '));}else{';
557 $html .= 'jQuery(' . $jq_placeholder . ').html(' . wp_json_encode( $this->html_age_date() ) . ');' . PHP_EOL;
558 $html .= 'jQuery(' . wp_json_encode( '#' . $video_placeholder_id . ' input[type=submit]' ) . ').one("click", function(event){jQuery.VideoPress.requirements.isSufficientAge(jQuery(' . $jq_container . '),' . absint( $this->video->age_rating ) . ')});' . PHP_EOL;
559 $html .= '}}}' . PHP_EOL;
560 } else {
561 $html .= "if ( jQuery.VideoPress.video.prepare({$guid_js}, {$player_config}," . self::$shown[ $guid ] . ') ) {' . PHP_EOL;
562 if ( isset( $this->options['autoplay'] ) && $this->options['autoplay'] === true ) {
563 $html .= "jQuery.VideoPress.video.play(jQuery({$jq_container}));";
564 } else {
565 $html .= 'jQuery(' . $jq_placeholder . ').one("click",function(){jQuery.VideoPress.video.play(jQuery(' . $jq_container . '))});';
566 }
567 $html .= '}';
568
569 // close the jQuery(document).ready() function
570 $html .= '});';
571 }
572 $html .= '</script>' . PHP_EOL;
573 $html .= '</div>' . PHP_EOL;
574
575 /*
576 * JavaScript required
577 */
578 $noun = __( 'this video', 'jetpack' );
579 if ( ! $age_gate_required ) {
580 $vid_type = '';
581 if ( ( isset( $this->options['freedom'] ) && $this->options['freedom'] === true ) && ( isset( $this->video->videos->ogv ) && isset( $this->video->videos->ogv->url ) ) ) {
582 $vid_type = 'ogv';
583 } elseif ( isset( $this->video->videos->mp4 ) && isset( $this->video->videos->mp4->url ) ) {
584 $vid_type = 'mp4';
585 } elseif ( isset( $this->video->videos->ogv ) && isset( $this->video->videos->ogv->url ) ) {
586 $vid_type = 'ogv';
587 }
588
589 if ( $vid_type !== '' ) {
590 $noun = '<a ';
591 if ( isset( $this->video->language ) ) {
592 $noun .= 'hreflang="' . esc_attr( $this->video->language ) . '" ';
593 }
594 if ( $vid_type === 'mp4' ) {
595 $noun .= 'type="video/mp4" href="' . esc_url( $this->video->videos->mp4->url, array( 'http', 'https' ) );
596 } elseif ( $vid_type === 'ogv' ) {
597 $noun .= 'type="video/ogv" href="' . esc_url( $this->video->videos->ogv->url, array( 'http', 'https' ) );
598 }
599 $noun .= '">';
600 if ( isset( $this->video->title ) ) {
601 $noun .= esc_html( $this->video->title );
602 } else {
603 $noun .= __( 'this video', 'jetpack' );
604 }
605 $noun .= '</a>';
606 } elseif ( ! empty( $this->title ) ) {
607 $noun = esc_html( $this->title );
608 }
609 unset( $vid_type );
610 }
611 /* translators: %s video title or generic 'this video' string */
612 $html .= '<noscript><p>' . sprintf( _x( 'JavaScript required to play %s.', 'Play as in playback or view a movie', 'jetpack' ), $noun ) . '</p></noscript>';
613
614 return $html;
615 }
616
617 /**
618 * Output for the non-legacy HTML5 player.
619 */
620 public function html5_dynamic_next() {
621 $video_container_id = 'v-' . $this->video->guid;
622
623 Jwt_Token_Bridge::enqueue_jwt_token_bridge();
624
625 // Must not use iframes for IE11 due to a fullscreen bug
626 if ( isset( $_SERVER['HTTP_USER_AGENT'] ) && stristr( sanitize_text_field( wp_unslash( $_SERVER['HTTP_USER_AGENT'] ) ), 'Trident/7.0; rv:11.0' ) ) {
627 $iframe_embed = false;
628 } else {
629
630 /**
631 * Filter the VideoPress iframe embed
632 *
633 * This filter allows you to control whether the videos will be embedded using an iframe.
634 * Set this to false in order to use an in-page embed rather than an iframe.
635 *
636 * @module videopress
637 *
638 * @since 3.7.0
639 *
640 * @param boolean $videopress_player_use_iframe
641 */
642 $iframe_embed = apply_filters( 'jetpack_videopress_player_use_iframe', true );
643 }
644
645 if ( ! array_key_exists( 'hd', $this->options ) ) {
646 $this->options['hd'] = (bool) get_option( 'video_player_high_quality', false );
647 }
648
649 if ( ! array_key_exists( 'cover', $this->options ) ) {
650 $this->options['cover'] = true;
651 }
652
653 $videopress_options = array(
654 'width' => absint( $this->video->calculated_width ),
655 'height' => absint( $this->video->calculated_height ),
656 );
657 foreach ( $this->options as $option => $value ) {
658 switch ( $option ) {
659 case 'at':
660 if ( (int) $value ) {
661 $videopress_options[ $option ] = (int) $value;
662 }
663 break;
664 case 'autoplay':
665 $option = 'autoPlay'; // Fall-through ok.
666 case 'hd':
667 case 'loop':
668 case 'permalink':
669 case 'cover':
670 case 'muted':
671 case 'controls':
672 case 'playsinline':
673 case 'useAverageColor':
674 if ( in_array( $value, array( true, 1, 'true' ), true ) ) {
675 $videopress_options[ $option ] = true;
676 } elseif ( in_array( $value, array( false, 0, 'false' ), true ) ) {
677 $videopress_options[ $option ] = false;
678 }
679 // phpcs:enable
680 break;
681 case 'defaultlangcode':
682 $option = 'defaultLangCode';
683 if ( $value ) {
684 $videopress_options[ $option ] = $value;
685 }
686 break;
687 case 'preloadContent':
688 if ( $value ) {
689 $videopress_options['preloadContent'] = $value;
690 }
691 }
692 }
693
694 if ( $iframe_embed ) {
695 $iframe_url = "https://videopress.com/embed/{$this->video->guid}";
696
697 foreach ( $videopress_options as $option => $value ) {
698 if ( ! in_array( $option, array( 'width', 'height' ), true ) ) {
699
700 // add_query_arg ignores false as a value, so replacing it with 0
701 $iframe_url = add_query_arg( $option, ( false === $value ) ? 0 : $value, $iframe_url );
702 }
703 }
704
705 $cover = $videopress_options['cover'] ? ' data-resize-to-parent="true"' : '';
706 $js_url = 'https://s0.wp.com/wp-content/plugins/video/assets/js/next/videopress-iframe.js';
707 // phpcs:disable WordPress.WP.EnqueuedResources.NonEnqueuedScript
708 return "<iframe title='" . __( 'VideoPress Video Player', 'jetpack' )
709 . "' aria-label='" . __( 'VideoPress Video Player', 'jetpack' )
710 . "' width='" . esc_attr( $videopress_options['width'] )
711 . "' height='" . esc_attr( $videopress_options['height'] )
712 . "' src='" . esc_attr( $iframe_url )
713 . "' frameborder='0' allowfullscreen"
714 . $cover
715 . " allow='clipboard-write'></iframe>"
716 . "<script src='" . esc_attr( $js_url ) . "'></script>";
717
718 } else {
719 $videopress_options = wp_json_encode( $videopress_options );
720 $js_url = 'https://s0.wp.com/wp-content/plugins/video/assets/js/videojs/videopress.js';
721
722 return "<div id='{$video_container_id}'></div>
723 <script src='{$js_url}'></script>
724 <script>
725 videopress('{$this->video->guid}', document.querySelector('#{$video_container_id}'), {$videopress_options});
726 </script>";
727 // phpcs:enable WordPress.WP.EnqueuedResources.NonEnqueuedScript
728 }
729 }
730
731 /**
732 * Only allow legitimate Flash parameters and their values
733 *
734 * @since 1.2
735 * @link https://helpx.adobe.com/flash/kb/flash-object-embed-tag-attributes.html Flash object and embed attributes
736 * @link https://helpx.adobe.com/flash/kb/font-outlines-device-fonts.html devicefont
737 * @link https://helpx.adobe.com/flash/kb/control-access-scripts-host-web.html allowscriptaccess
738 * @link https://www.adobe.com/devnet/flashplayer/articles/full_screen_mode.html full screen mode
739 * @link https://help.adobe.com/en_US/as3/dev/WS1EFE2EDA-026D-4d14-864E-79DFD56F87C6.html allownetworking
740 * @param array $flash_params Flash parameters expressed in key-value form.
741 * @return array validated Flash parameters
742 */
743 public static function esc_flash_params( $flash_params ) {
744 $allowed_params = array(
745 'swliveconnect' => array( 'true', 'false' ),
746 'play' => array( 'true', 'false' ),
747 'loop' => array( 'true', 'false' ),
748 'menu' => array( 'true', 'false' ),
749 'quality' => array( 'low', 'autolow', 'autohigh', 'medium', 'high', 'best' ),
750 'scale' => array( 'default', 'noborder', 'exactfit', 'noscale' ),
751 'align' => array( 'l', 'r', 't' ),
752 'salign' => array( 'l', 'r', 't', 'tl', 'tr', 'bl', 'br' ),
753 'wmode' => array( 'window', 'opaque', 'transparent', 'direct', 'gpu' ),
754 'devicefont' => array( '_sans', '_serif', '_typewriter' ),
755 'allowscriptaccess' => array( 'always', 'samedomain', 'never' ),
756 'allownetworking' => array( 'all', 'internal', 'none' ),
757 'seamlesstabbing' => array( 'true', 'false' ),
758 'allowfullscreen' => array( 'true', 'false' ),
759 'fullScreenAspectRatio' => array( 'portrait', 'landscape' ),
760 'base',
761 'bgcolor',
762 'flashvars',
763 );
764
765 $allowed_params_keys = array_keys( $allowed_params );
766
767 $filtered_params = array();
768 foreach ( $flash_params as $param => $value ) {
769 if ( empty( $param ) || empty( $value ) ) {
770 continue;
771 }
772 $param = strtolower( $param );
773 if ( in_array( $param, $allowed_params_keys, true ) ) {
774 if ( isset( $allowed_params[ $param ] ) && is_array( $allowed_params[ $param ] ) ) {
775 $value = strtolower( $value );
776 if ( in_array( $value, $allowed_params[ $param ], true ) ) {
777 $filtered_params[ $param ] = $value;
778 }
779 } else {
780 $filtered_params[ $param ] = $value;
781 }
782 }
783 }
784 unset( $allowed_params_keys );
785
786 /**
787 * Flash specifies sameDomain, not samedomain. change from lowercase value for preciseness
788 */
789 if ( isset( $filtered_params['allowscriptaccess'] ) && $filtered_params['allowscriptaccess'] === 'samedomain' ) {
790 $filtered_params['allowscriptaccess'] = 'sameDomain';
791 }
792
793 return $filtered_params;
794 }
795
796 /**
797 * Filter Flash variables from the response, taking into consideration player options.
798 *
799 * @since 1.3
800 * @return array Flash variable key value pairs
801 */
802 private function get_flash_variables() {
803 if ( ! isset( $this->video->players->swf->vars ) ) {
804 return array();
805 }
806
807 $flashvars = (array) $this->video->players->swf->vars;
808 if ( isset( $this->options['autoplay'] ) && $this->options['autoplay'] === true ) {
809 $flashvars['autoPlay'] = 'true';
810 }
811 return $flashvars;
812 }
813
814 /**
815 * Validate and filter Flash parameters
816 *
817 * @since 1.3
818 * @return array Flash parameters passed through key and value validation
819 */
820 private function get_flash_parameters() {
821 if ( ! isset( $this->video->players->swf->params ) ) {
822 return array();
823 } else {
824 return self::esc_flash_params(
825 /**
826 * Filters the Flash parameters of the VideoPress player.
827 *
828 * @module videopress
829 *
830 * @since 1.2.0
831 *
832 * @param array $this->video->players->swf->params Array of swf parameters for the VideoPress flash player.
833 */
834 apply_filters( 'video_flash_params', (array) $this->video->players->swf->params, 10, 1 )
835 );
836 }
837 }
838
839 /**
840 * Flash player markup in a HTML embed element.
841 *
842 * @since 1.1
843 * @link https://html.spec.whatwg.org/multipage/iframe-embed-object.html#the-embed-element embed element
844 * @link http://www.google.com/support/reader/bin/answer.py?answer=70664 Google Reader markup support
845 * @return string HTML markup. Embed element with no children
846 */
847 private function flash_embed() {
848 wp_enqueue_script( 'videopress' );
849 if ( ! isset( $this->video->players->swf ) || ! isset( $this->video->players->swf->url ) ) {
850 return '';
851 }
852
853 $embed = array(
854 'id' => $this->video_id,
855 'src' => esc_url_raw( $this->video->players->swf->url . '&' . http_build_query( $this->get_flash_variables(), null, '&' ), array( 'http', 'https' ) ),
856 'type' => 'application/x-shockwave-flash',
857 'width' => $this->video->calculated_width,
858 'height' => $this->video->calculated_height,
859 );
860 if ( isset( $this->video->title ) ) {
861 $embed['title'] = $this->video->title;
862 }
863 $embed = array_merge( $embed, $this->get_flash_parameters() );
864
865 $html = '<embed';
866 foreach ( $embed as $attribute => $value ) {
867 $html .= ' ' . esc_html( $attribute ) . '="' . esc_attr( $value ) . '"';
868 }
869 unset( $embed );
870 $html .= '></embed>';
871 return $html;
872 }
873
874 /**
875 * Double-baked Flash object markup for Internet Explorer and more standards-friendly consuming agents.
876 *
877 * @since 1.1
878 * @return string HTML markup. Object and children.
879 */
880 private function flash_object() {
881 wp_enqueue_script( 'videopress' );
882 if ( ! isset( $this->video->players->swf ) || ! isset( $this->video->players->swf->url ) ) {
883 return '';
884 }
885
886 $thumbnail_html = '<img alt="';
887 if ( isset( $this->video->title ) ) {
888 $thumbnail_html .= esc_attr( $this->video->title );
889 }
890 $thumbnail_html .= '" src="' . esc_url( $this->video->poster_frame_uri, array( 'http', 'https' ) ) . '" width="' . $this->video->calculated_width . '" height="' . $this->video->calculated_height . '" />';
891 $flash_vars = esc_attr( http_build_query( $this->get_flash_variables(), null, '&' ) );
892 $flash_params = '';
893 foreach ( $this->get_flash_parameters() as $attribute => $value ) {
894 $flash_params .= '<param name="' . esc_attr( $attribute ) . '" value="' . esc_attr( $value ) . '" />';
895 }
896 /* translators: %s url to the Adobe Flash Player website */
897 $flash_help = sprintf( __( 'This video requires <a rel="nofollow noopener noreferrer" href="%s" target="_blank">Adobe Flash</a> for playback.', 'jetpack' ), 'https://get.adobe.com/flashplayer/' );
898 $flash_player_url = esc_url( $this->video->players->swf->url, array( 'http', 'https' ) );
899 $description = '';
900 if ( isset( $this->video->title ) ) {
901 $standby = $this->video->title;
902 $description = '<p><strong>' . esc_html( $this->video->title ) . '</strong></p>';
903 } else {
904 $standby = __( 'Loading video...', 'jetpack' );
905 }
906 $standby = ' standby="' . esc_attr( $standby ) . '"';
907 return <<<OBJECT
908 <script type="text/javascript">if(typeof swfobject!=="undefined"){swfobject.registerObject("{$this->video_id}", "{$this->video->players->swf->version}");}</script>
909 <object classid="clsid:D27CDB6E-AE6D-11cf-96B8-444553540000" width="{$this->video->calculated_width}" height="{$this->video->calculated_height}" id="{$this->video_id}"{$standby}>
910 <param name="movie" value="{$flash_player_url}" />
911 {$flash_params}
912 <param name="flashvars" value="{$flash_vars}" />
913 <!--[if !IE]>-->
914 <object type="application/x-shockwave-flash" data="{$flash_player_url}" width="{$this->video->calculated_width}" height="{$this->video->calculated_height}"{$standby}>
915 {$flash_params}
916 <param name="flashvars" value="{$flash_vars}" />
917 <!--<![endif]-->
918 {$thumbnail_html}{$description}<p class="robots-nocontent">{$flash_help}</p>
919 <!--[if !IE]>-->
920 </object>
921 <!--<![endif]-->
922 </object>
923 OBJECT;
924 }
925 }
926