PluginProbe ʕ •ᴥ•ʔ
Jetpack – WP Security, Backup, Speed, & Growth / 12.8.3
Jetpack – WP Security, Backup, Speed, & Growth v12.8.3
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 3 years ago js 3 years ago class-videopress-attachment-metadata.php 4 years ago class.jetpack-videopress.php 4 years ago class.videopress-cli.php 3 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 3 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 3 years ago shortcode.php 2 years ago videopress-admin-rtl.css 4 years ago videopress-admin-rtl.min.css 4 years ago videopress-admin.css 4 years ago videopress-admin.min.css 4 years ago
class.videopress-player.php
920 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 if ( isset( $this->options['autoplay'] ) && $this->options['autoplay'] === true ) {
333 $html .= ' autoplay="true"';
334 } else {
335 $html .= ' preload="' . $this->options['preloadContent'] . '"';
336 }
337 if ( isset( $this->video->text_direction ) ) {
338 $html .= ' dir="' . esc_attr( $this->video->text_direction ) . '"';
339 }
340 if ( isset( $this->video->language ) ) {
341 $html .= ' lang="' . esc_attr( $this->video->language ) . '"';
342 }
343 $html .= '>';
344 if (
345 ( ! isset( $this->options['freedom'] ) || $this->options['freedom'] === false )
346 && isset( $this->video->videos->mp4 )
347 ) {
348 $mp4 = $this->video->videos->mp4->url;
349 if ( ! empty( $mp4 ) ) {
350 $html .= '<source src="' . esc_url( $mp4 ) . '" type="video/mp4; codecs=&quot;' . esc_attr( $this->video->videos->mp4->codecs ) . '&quot;" />';
351 }
352 unset( $mp4 );
353 }
354
355 if ( isset( $this->video->videos->ogv ) ) {
356 $ogg = $this->video->videos->ogv->url;
357 if ( ! empty( $ogg ) ) {
358 $html .= '<source src="' . esc_url( $ogg ) . '" type="video/ogg; codecs=&quot;' . esc_attr( $this->video->videos->ogv->codecs ) . '&quot;" />';
359 }
360
361 unset( $ogg );
362 }
363
364 $html .= '<div><img alt="';
365 if ( isset( $this->video->title ) ) {
366 $html .= esc_attr( $this->video->title );
367 }
368 $html .= '" src="' . $thumbnail . '" width="' . $this->video->calculated_width . '" height="' . $this->video->calculated_height . '" /></div>';
369 if ( isset( $this->options['freedom'] ) && $this->options['freedom'] === true ) {
370 /* translators: %s url to the gnu.org website */
371 $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>';
372 } elseif ( isset( $this->video->title ) ) {
373 $html .= '<p>' . esc_html( $this->video->title ) . '</p>';
374 }
375 $html .= '</video>';
376 return $html;
377 }
378
379 /**
380 * Click to play dynamic HTML5-capable player.
381 * The player displays a video preview section including poster frame,
382 * video title, play button and watermark on the original page load
383 * and calculates the playback capabilities of the browser. The video player
384 * is loaded when the visitor clicks on the video preview area.
385 * If Flash Player 10 or above is available the browser will display
386 * the Flash version of the video. If HTML5 video appears to be supported
387 * and the browser may be capable of MP4 (H.264, AAC) or OGV (Theora, Vorbis)
388 * playback the browser will display its native HTML5 player.
389 *
390 * @since 1.5
391 * @return string HTML markup
392 */
393 private function html5_dynamic() {
394
395 /**
396 * Filter the VideoPress legacy player feature
397 *
398 * This filter allows you to control whether the legacy VideoPress player should be used
399 * instead of the improved one.
400 *
401 * @module videopress
402 *
403 * @since 3.7.0
404 *
405 * @param boolean $videopress_use_legacy_player
406 */
407 if ( ! apply_filters( 'jetpack_videopress_use_legacy_player', false ) ) {
408 return $this->html5_dynamic_next();
409 }
410
411 wp_enqueue_script( 'videopress' );
412 $video_placeholder_id = $this->video_container_id . '-placeholder';
413 $age_gate_required = $this->age_gate_required();
414 $width = absint( $this->video->calculated_width );
415 $height = absint( $this->video->calculated_height );
416
417 $html = '<div id="' . $video_placeholder_id . '" class="videopress-placeholder" style="';
418 if ( $age_gate_required ) {
419 $html .= "min-width:{$width}px;min-height:{$height}px";
420 } else {
421 $html .= "width:{$width}px;height:{$height}px";
422 }
423 $html .= ';display:none;cursor:pointer !important;position:relative;';
424 if ( isset( $this->video->skin ) && isset( $this->video->skin->background_color ) ) {
425 $html .= 'background-color:' . esc_attr( $this->video->skin->background_color ) . ';';
426 }
427 $html .= 'font-family: \'Helvetica Neue\',Arial,Helvetica,\'Nimbus Sans L\',sans-serif;font-weight:bold;font-size:18px">' . PHP_EOL;
428
429 /**
430 * Do not display a poster frame, title, or any other content hints for mature content.
431 */
432 if ( ! $age_gate_required ) {
433 if ( ! empty( $this->video->title ) ) {
434 $html .= '<div class="videopress-title" style="display:inline;position:absolute;margin:20px 20px 0 20px;padding:4px 8px;vertical-align:top;text-align:';
435 if ( $this->video->text_direction === 'rtl' ) {
436 $html .= 'right" dir="rtl"';
437 } else {
438 $html .= 'left" dir="ltr"';
439 }
440 if ( isset( $this->video->language ) ) {
441 $html .= ' lang="' . esc_attr( $this->video->language ) . '"';
442 }
443 $html .= '><span style="padding:3px 0;line-height:1.5em;';
444 if ( isset( $this->video->skin ) && isset( $this->video->skin->background_color ) ) {
445 $html .= 'background-color:';
446 if ( $this->video->skin->background_color === 'rgb(0,0,0)' ) {
447 $html .= 'rgba(0,0,0,0.8)';
448 } else {
449 $html .= esc_attr( $this->video->skin->background_color );
450 }
451 $html .= ';';
452 }
453 $html .= 'color:rgb(255,255,255)">' . esc_html( $this->video->title ) . '</span></div>';
454 }
455 $html .= '<img class="videopress-poster" alt="';
456 if ( ! empty( $this->video->title ) ) {
457 /* translators: %s is the video title */
458 $html .= esc_attr( $this->video->title ) . '" title="' . esc_attr( sprintf( _x( 'Watch: %s', 'watch a video title', 'jetpack' ), $this->video->title ) );
459 }
460 $html .= '" src="' . esc_url( $this->video->poster_frame_uri, array( 'http', 'https' ) ) . '" width="' . $width . '" height="' . $height . '" />' . PHP_EOL;
461
462 // style a play button hovered over the poster frame
463 $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;
464
465 // watermark
466 if ( isset( $this->video->skin ) && isset( $this->video->skin->watermark ) ) {
467 $html .= '<div style="position:relative;margin-top:-40px;height:25px;margin-bottom:35px;';
468 if ( $this->video->text_direction === 'rtl' ) {
469 $html .= 'margin-left:20px;text-align:left;';
470 } else {
471 $html .= 'margin-right:20px;text-align:right;';
472 }
473 $html .= 'vertical-align:bottom;z-index:3">';
474 $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"/>';
475 $html .= '</div>' . PHP_EOL;
476 }
477 }
478
479 $data = array(
480 'blog' => absint( $this->video->blog_id ),
481 'post' => absint( $this->video->post_id ),
482 'duration' => absint( $this->video->duration ),
483 'poster' => esc_url_raw( $this->video->poster_frame_uri, array( 'http', 'https' ) ),
484 'hd' => (bool) $this->options['hd'],
485 );
486 if ( isset( $this->video->videos ) ) {
487 if ( isset( $this->video->videos->mp4 ) && isset( $this->video->videos->mp4->url ) ) {
488 $data['mp4'] = array(
489 'size' => $this->video->videos->mp4->format,
490 'uri' => esc_url_raw( $this->video->videos->mp4->url, array( 'http', 'https' ) ),
491 );
492 }
493 if ( isset( $this->video->videos->ogv ) && isset( $this->video->videos->ogv->url ) ) {
494 $data['ogv'] = array(
495 'size' => 'std',
496 'uri' => esc_url_raw( $this->video->videos->ogv->url, array( 'http', 'https' ) ),
497 );
498 }
499 }
500 $locale = array( 'dir' => $this->video->text_direction );
501 if ( isset( $this->video->language ) ) {
502 $locale['lang'] = $this->video->language;
503 }
504 $data['locale'] = $locale;
505 unset( $locale );
506
507 $guid = $this->video->guid;
508 $guid_js = wp_json_encode( $guid );
509 $html .= '<script type="text/javascript">' . PHP_EOL;
510 $html .= 'jQuery(document).ready(function() {';
511
512 $html .= 'if ( !jQuery.VideoPress.data[' . wp_json_encode( $guid ) . '] ) { jQuery.VideoPress.data[' . wp_json_encode( $guid ) . '] = new Array(); }' . PHP_EOL;
513 $html .= 'jQuery.VideoPress.data[' . wp_json_encode( $guid ) . '][' . self::$shown[ $guid ] . ']=' . wp_json_encode( $data ) . ';' . PHP_EOL;
514 unset( $data );
515
516 $jq_container = wp_json_encode( '#' . $this->video_container_id );
517 $jq_placeholder = wp_json_encode( '#' . $video_placeholder_id );
518 $player_config = "{width:{$width},height:{$height},";
519 if ( isset( $this->options['freedom'] ) && $this->options['freedom'] === true ) {
520 $player_config .= 'freedom:"true",';
521 }
522 $player_config .= 'container:jQuery(' . $jq_container . ')}';
523
524 $html .= "jQuery({$jq_placeholder}).show(0,function(){jQuery.VideoPress.analytics.impression({$guid_js})});" . PHP_EOL;
525
526 if ( $age_gate_required ) {
527 $html .= 'if ( jQuery.VideoPress.support.flash() ) {' . PHP_EOL;
528 /**
529 * Insert alternative content for Flash players.
530 *
531 * @link https://github.com/swfobject/swfobject/wiki/SWFObject-API#swfobjectembedswfswfurlstr-replaceelemidstr-widthstr-heightstr-swfversionstr-xiswfurlstr-flashvarsobj-parobj-attobj-callbackfn
532 */
533 $html .= 'swfobject.embedSWF(' . implode(
534 ',',
535 array(
536 'jQuery.VideoPress.video.flash.player_uri',
537 wp_json_encode( $this->video_container_id ),
538 wp_json_encode( $width ),
539 wp_json_encode( $height ),
540 'jQuery.VideoPress.video.flash.min_version',
541 '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
542 '{guid:' . $guid_js . '}', // FlashVars
543 'jQuery.VideoPress.video.flash.params',
544 'null', // no attributes
545 'jQuery.VideoPress.video.flash.embedCallback', // error fallback
546 )
547 ) . ');';
548 $html .= '} else {' . PHP_EOL;
549 $html .= "if ( jQuery.VideoPress.video.prepare({$guid_js},{$player_config}," . self::$shown[ $guid ] . ') ) {' . PHP_EOL;
550 $html .= 'if ( jQuery(' . $jq_container . ').data( "player" ) === "flash" ){jQuery.VideoPress.video.play(jQuery(' . wp_json_encode( '#' . $this->video_container_id ) . '));}else{';
551 $html .= 'jQuery(' . $jq_placeholder . ').html(' . wp_json_encode( $this->html_age_date() ) . ');' . PHP_EOL;
552 $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;
553 $html .= '}}}' . PHP_EOL;
554 } else {
555 $html .= "if ( jQuery.VideoPress.video.prepare({$guid_js}, {$player_config}," . self::$shown[ $guid ] . ') ) {' . PHP_EOL;
556 if ( isset( $this->options['autoplay'] ) && $this->options['autoplay'] === true ) {
557 $html .= "jQuery.VideoPress.video.play(jQuery({$jq_container}));";
558 } else {
559 $html .= 'jQuery(' . $jq_placeholder . ').one("click",function(){jQuery.VideoPress.video.play(jQuery(' . $jq_container . '))});';
560 }
561 $html .= '}';
562
563 // close the jQuery(document).ready() function
564 $html .= '});';
565 }
566 $html .= '</script>' . PHP_EOL;
567 $html .= '</div>' . PHP_EOL;
568
569 /*
570 * JavaScript required
571 */
572 $noun = __( 'this video', 'jetpack' );
573 if ( ! $age_gate_required ) {
574 $vid_type = '';
575 if ( ( isset( $this->options['freedom'] ) && $this->options['freedom'] === true ) && ( isset( $this->video->videos->ogv ) && isset( $this->video->videos->ogv->url ) ) ) {
576 $vid_type = 'ogv';
577 } elseif ( isset( $this->video->videos->mp4 ) && isset( $this->video->videos->mp4->url ) ) {
578 $vid_type = 'mp4';
579 } elseif ( isset( $this->video->videos->ogv ) && isset( $this->video->videos->ogv->url ) ) {
580 $vid_type = 'ogv';
581 }
582
583 if ( $vid_type !== '' ) {
584 $noun = '<a ';
585 if ( isset( $this->video->language ) ) {
586 $noun .= 'hreflang="' . esc_attr( $this->video->language ) . '" ';
587 }
588 if ( $vid_type === 'mp4' ) {
589 $noun .= 'type="video/mp4" href="' . esc_url( $this->video->videos->mp4->url, array( 'http', 'https' ) );
590 } elseif ( $vid_type === 'ogv' ) {
591 $noun .= 'type="video/ogv" href="' . esc_url( $this->video->videos->ogv->url, array( 'http', 'https' ) );
592 }
593 $noun .= '">';
594 if ( isset( $this->video->title ) ) {
595 $noun .= esc_html( $this->video->title );
596 } else {
597 $noun .= __( 'this video', 'jetpack' );
598 }
599 $noun .= '</a>';
600 } elseif ( ! empty( $this->title ) ) {
601 $noun = esc_html( $this->title );
602 }
603 unset( $vid_type );
604 }
605 /* translators: %s video title or generic 'this video' string */
606 $html .= '<noscript><p>' . sprintf( _x( 'JavaScript required to play %s.', 'Play as in playback or view a movie', 'jetpack' ), $noun ) . '</p></noscript>';
607
608 return $html;
609 }
610
611 /**
612 * Output for the non-legacy HTML5 player.
613 */
614 public function html5_dynamic_next() {
615 $video_container_id = 'v-' . $this->video->guid;
616
617 Jwt_Token_Bridge::enqueue_jwt_token_bridge();
618
619 // Must not use iframes for IE11 due to a fullscreen bug
620 if ( isset( $_SERVER['HTTP_USER_AGENT'] ) && stristr( sanitize_text_field( wp_unslash( $_SERVER['HTTP_USER_AGENT'] ) ), 'Trident/7.0; rv:11.0' ) ) {
621 $iframe_embed = false;
622 } else {
623
624 /**
625 * Filter the VideoPress iframe embed
626 *
627 * This filter allows you to control whether the videos will be embedded using an iframe.
628 * Set this to false in order to use an in-page embed rather than an iframe.
629 *
630 * @module videopress
631 *
632 * @since 3.7.0
633 *
634 * @param boolean $videopress_player_use_iframe
635 */
636 $iframe_embed = apply_filters( 'jetpack_videopress_player_use_iframe', true );
637 }
638
639 if ( ! array_key_exists( 'hd', $this->options ) ) {
640 $this->options['hd'] = (bool) get_option( 'video_player_high_quality', false );
641 }
642
643 if ( ! array_key_exists( 'cover', $this->options ) ) {
644 $this->options['cover'] = true;
645 }
646
647 $videopress_options = array(
648 'width' => absint( $this->video->calculated_width ),
649 'height' => absint( $this->video->calculated_height ),
650 );
651 foreach ( $this->options as $option => $value ) {
652 switch ( $option ) {
653 case 'at':
654 if ( (int) $value ) {
655 $videopress_options[ $option ] = (int) $value;
656 }
657 break;
658 case 'autoplay':
659 $option = 'autoPlay'; // Fall-through ok.
660 case 'hd':
661 case 'loop':
662 case 'permalink':
663 case 'cover':
664 case 'muted':
665 case 'controls':
666 case 'playsinline':
667 case 'useAverageColor':
668 if ( in_array( $value, array( true, 1, 'true' ), true ) ) {
669 $videopress_options[ $option ] = true;
670 } elseif ( in_array( $value, array( false, 0, 'false' ), true ) ) {
671 $videopress_options[ $option ] = false;
672 }
673 // phpcs:enable
674 break;
675 case 'defaultlangcode':
676 $option = 'defaultLangCode';
677 if ( $value ) {
678 $videopress_options[ $option ] = $value;
679 }
680 break;
681 case 'preloadContent':
682 if ( $value ) {
683 $videopress_options['preloadContent'] = $value;
684 }
685 }
686 }
687
688 if ( $iframe_embed ) {
689 $iframe_url = "https://videopress.com/embed/{$this->video->guid}";
690
691 foreach ( $videopress_options as $option => $value ) {
692 if ( ! in_array( $option, array( 'width', 'height' ), true ) ) {
693
694 // add_query_arg ignores false as a value, so replacing it with 0
695 $iframe_url = add_query_arg( $option, ( false === $value ) ? 0 : $value, $iframe_url );
696 }
697 }
698
699 $cover = $videopress_options['cover'] ? ' data-resize-to-parent="true"' : '';
700 $js_url = 'https://s0.wp.com/wp-content/plugins/video/assets/js/next/videopress-iframe.js';
701 // phpcs:disable WordPress.WP.EnqueuedResources.NonEnqueuedScript
702 return "<iframe title='" . __( 'VideoPress Video Player', 'jetpack' )
703 . "' aria-label='" . __( 'VideoPress Video Player', 'jetpack' )
704 . "' width='" . esc_attr( $videopress_options['width'] )
705 . "' height='" . esc_attr( $videopress_options['height'] )
706 . "' src='" . esc_attr( $iframe_url )
707 . "' frameborder='0' allowfullscreen"
708 . $cover
709 . " allow='clipboard-write'></iframe>"
710 . "<script src='" . esc_attr( $js_url ) . "'></script>";
711
712 } else {
713 $videopress_options = wp_json_encode( $videopress_options );
714 $js_url = 'https://s0.wp.com/wp-content/plugins/video/assets/js/videojs/videopress.js';
715
716 return "<div id='{$video_container_id}'></div>
717 <script src='{$js_url}'></script>
718 <script>
719 videopress('{$this->video->guid}', document.querySelector('#{$video_container_id}'), {$videopress_options});
720 </script>";
721 // phpcs:enable WordPress.WP.EnqueuedResources.NonEnqueuedScript
722 }
723 }
724
725 /**
726 * Only allow legitimate Flash parameters and their values
727 *
728 * @since 1.2
729 * @link https://helpx.adobe.com/flash/kb/flash-object-embed-tag-attributes.html Flash object and embed attributes
730 * @link https://helpx.adobe.com/flash/kb/font-outlines-device-fonts.html devicefont
731 * @link https://helpx.adobe.com/flash/kb/control-access-scripts-host-web.html allowscriptaccess
732 * @link https://www.adobe.com/devnet/flashplayer/articles/full_screen_mode.html full screen mode
733 * @link https://help.adobe.com/en_US/as3/dev/WS1EFE2EDA-026D-4d14-864E-79DFD56F87C6.html allownetworking
734 * @param array $flash_params Flash parameters expressed in key-value form.
735 * @return array validated Flash parameters
736 */
737 public static function esc_flash_params( $flash_params ) {
738 $allowed_params = array(
739 'swliveconnect' => array( 'true', 'false' ),
740 'play' => array( 'true', 'false' ),
741 'loop' => array( 'true', 'false' ),
742 'menu' => array( 'true', 'false' ),
743 'quality' => array( 'low', 'autolow', 'autohigh', 'medium', 'high', 'best' ),
744 'scale' => array( 'default', 'noborder', 'exactfit', 'noscale' ),
745 'align' => array( 'l', 'r', 't' ),
746 'salign' => array( 'l', 'r', 't', 'tl', 'tr', 'bl', 'br' ),
747 'wmode' => array( 'window', 'opaque', 'transparent', 'direct', 'gpu' ),
748 'devicefont' => array( '_sans', '_serif', '_typewriter' ),
749 'allowscriptaccess' => array( 'always', 'samedomain', 'never' ),
750 'allownetworking' => array( 'all', 'internal', 'none' ),
751 'seamlesstabbing' => array( 'true', 'false' ),
752 'allowfullscreen' => array( 'true', 'false' ),
753 'fullScreenAspectRatio' => array( 'portrait', 'landscape' ),
754 'base',
755 'bgcolor',
756 'flashvars',
757 );
758
759 $allowed_params_keys = array_keys( $allowed_params );
760
761 $filtered_params = array();
762 foreach ( $flash_params as $param => $value ) {
763 if ( empty( $param ) || empty( $value ) ) {
764 continue;
765 }
766 $param = strtolower( $param );
767 if ( in_array( $param, $allowed_params_keys, true ) ) {
768 if ( isset( $allowed_params[ $param ] ) && is_array( $allowed_params[ $param ] ) ) {
769 $value = strtolower( $value );
770 if ( in_array( $value, $allowed_params[ $param ], true ) ) {
771 $filtered_params[ $param ] = $value;
772 }
773 } else {
774 $filtered_params[ $param ] = $value;
775 }
776 }
777 }
778 unset( $allowed_params_keys );
779
780 /**
781 * Flash specifies sameDomain, not samedomain. change from lowercase value for preciseness
782 */
783 if ( isset( $filtered_params['allowscriptaccess'] ) && $filtered_params['allowscriptaccess'] === 'samedomain' ) {
784 $filtered_params['allowscriptaccess'] = 'sameDomain';
785 }
786
787 return $filtered_params;
788 }
789
790 /**
791 * Filter Flash variables from the response, taking into consideration player options.
792 *
793 * @since 1.3
794 * @return array Flash variable key value pairs
795 */
796 private function get_flash_variables() {
797 if ( ! isset( $this->video->players->swf->vars ) ) {
798 return array();
799 }
800
801 $flashvars = (array) $this->video->players->swf->vars;
802 if ( isset( $this->options['autoplay'] ) && $this->options['autoplay'] === true ) {
803 $flashvars['autoPlay'] = 'true';
804 }
805 return $flashvars;
806 }
807
808 /**
809 * Validate and filter Flash parameters
810 *
811 * @since 1.3
812 * @return array Flash parameters passed through key and value validation
813 */
814 private function get_flash_parameters() {
815 if ( ! isset( $this->video->players->swf->params ) ) {
816 return array();
817 } else {
818 return self::esc_flash_params(
819 /**
820 * Filters the Flash parameters of the VideoPress player.
821 *
822 * @module videopress
823 *
824 * @since 1.2.0
825 *
826 * @param array $this->video->players->swf->params Array of swf parameters for the VideoPress flash player.
827 */
828 apply_filters( 'video_flash_params', (array) $this->video->players->swf->params, 10, 1 )
829 );
830 }
831 }
832
833 /**
834 * Flash player markup in a HTML embed element.
835 *
836 * @since 1.1
837 * @link https://html.spec.whatwg.org/multipage/iframe-embed-object.html#the-embed-element embed element
838 * @link http://www.google.com/support/reader/bin/answer.py?answer=70664 Google Reader markup support
839 * @return string HTML markup. Embed element with no children
840 */
841 private function flash_embed() {
842 wp_enqueue_script( 'videopress' );
843 if ( ! isset( $this->video->players->swf ) || ! isset( $this->video->players->swf->url ) ) {
844 return '';
845 }
846
847 $embed = array(
848 'id' => $this->video_id,
849 'src' => esc_url_raw( $this->video->players->swf->url . '&' . http_build_query( $this->get_flash_variables(), null, '&' ), array( 'http', 'https' ) ),
850 'type' => 'application/x-shockwave-flash',
851 'width' => $this->video->calculated_width,
852 'height' => $this->video->calculated_height,
853 );
854 if ( isset( $this->video->title ) ) {
855 $embed['title'] = $this->video->title;
856 }
857 $embed = array_merge( $embed, $this->get_flash_parameters() );
858
859 $html = '<embed';
860 foreach ( $embed as $attribute => $value ) {
861 $html .= ' ' . esc_html( $attribute ) . '="' . esc_attr( $value ) . '"';
862 }
863 unset( $embed );
864 $html .= '></embed>';
865 return $html;
866 }
867
868 /**
869 * Double-baked Flash object markup for Internet Explorer and more standards-friendly consuming agents.
870 *
871 * @since 1.1
872 * @return HTML markup. Object and children.
873 */
874 private function flash_object() {
875 wp_enqueue_script( 'videopress' );
876 if ( ! isset( $this->video->players->swf ) || ! isset( $this->video->players->swf->url ) ) {
877 return '';
878 }
879
880 $thumbnail_html = '<img alt="';
881 if ( isset( $this->video->title ) ) {
882 $thumbnail_html .= esc_attr( $this->video->title );
883 }
884 $thumbnail_html .= '" src="' . esc_url( $this->video->poster_frame_uri, array( 'http', 'https' ) ) . '" width="' . $this->video->calculated_width . '" height="' . $this->video->calculated_height . '" />';
885 $flash_vars = esc_attr( http_build_query( $this->get_flash_variables(), null, '&' ) );
886 $flash_params = '';
887 foreach ( $this->get_flash_parameters() as $attribute => $value ) {
888 $flash_params .= '<param name="' . esc_attr( $attribute ) . '" value="' . esc_attr( $value ) . '" />';
889 }
890 /* translators: %s url to the Adobe Flash Player website */
891 $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/' );
892 $flash_player_url = esc_url( $this->video->players->swf->url, array( 'http', 'https' ) );
893 $description = '';
894 if ( isset( $this->video->title ) ) {
895 $standby = $this->video->title;
896 $description = '<p><strong>' . esc_html( $this->video->title ) . '</strong></p>';
897 } else {
898 $standby = __( 'Loading video...', 'jetpack' );
899 }
900 $standby = ' standby="' . esc_attr( $standby ) . '"';
901 return <<<OBJECT
902 <script type="text/javascript">if(typeof swfobject!=="undefined"){swfobject.registerObject("{$this->video_id}", "{$this->video->players->swf->version}");}</script>
903 <object classid="clsid:D27CDB6E-AE6D-11cf-96B8-444553540000" width="{$this->video->calculated_width}" height="{$this->video->calculated_height}" id="{$this->video_id}"{$standby}>
904 <param name="movie" value="{$flash_player_url}" />
905 {$flash_params}
906 <param name="flashvars" value="{$flash_vars}" />
907 <!--[if !IE]>-->
908 <object type="application/x-shockwave-flash" data="{$flash_player_url}" width="{$this->video->calculated_width}" height="{$this->video->calculated_height}"{$standby}>
909 {$flash_params}
910 <param name="flashvars" value="{$flash_vars}" />
911 <!--<![endif]-->
912 {$thumbnail_html}{$description}<p class="robots-nocontent">{$flash_help}</p>
913 <!--[if !IE]>-->
914 </object>
915 <!--<![endif]-->
916 </object>
917 OBJECT;
918 }
919 }
920