VideoPress_Player   F
last analyzed

Complexity

Total Complexity 152

Size/Duplication

Total Lines 874
Duplicated Lines 2.29 %

Coupling/Cohesion

Components 1
Dependencies 1

Importance

Changes 0
Metric Value
dl 20
loc 874
rs 1.726
c 0
b 0
f 0
wmc 152
lcom 1
cbo 1

15 Methods

Rating   Name   Duplication   Size   Complexity  
F __construct() 0 74 19
A html_wrapper() 0 7 2
A asXML() 0 14 5
B asHTML() 0 27 10
A error_message() 0 13 4
A age_gate_required() 0 7 3
B html_age_gate() 0 69 7
F html5_static() 10 46 14
F html5_dynamic() 4 213 42
F html5_dynamic_next() 0 88 19
B esc_flash_params() 0 52 10
A get_flash_variables() 2 11 4
A get_flash_parameters() 0 18 2
A flash_embed() 2 26 5
B flash_object() 2 44 6

How to fix   Duplicated Code    Complexity   

Duplicated Code

Duplicate code is one of the most pungent code smells. A rule that is often used is to re-structure code once it is duplicated in three or more places.

Common duplication problems, and corresponding solutions are:

Complex Class

 Tip:   Before tackling complexity, make sure that you eliminate any duplication first. This often can reduce the size of classes significantly.

Complex classes like VideoPress_Player often do a lot of different things. To break such a class down, we need to identify a cohesive component within that class. A common approach to find such a component is to look for fields/methods that share the same prefixes, or suffixes. You can also have a look at the cohesion graph to spot any un-connected, or weakly-connected components.

Once you have determined the fields that belong together, you can apply the Extract Class refactoring. If the component makes sense as a sub-class, Extract Subclass is also a candidate, and is often faster.

While breaking up the class, it is a good idea to analyze how other classes use VideoPress_Player, and based on these observations, apply Extract Interface, too.

1
<?php
2
/**
3
 * VideoPress playback module markup generator.
4
 *
5
 * @since 1.3
6
 */
7
class VideoPress_Player {
8
	/**
9
	 * Video data for the requested guid and maximum width
10
	 *
11
	 * @since 1.3
12
	 * @var VideoPress_Video
13
	 */
14
	protected $video;
15
16
	/**
17
	 * DOM identifier of the video container
18
	 *
19
	 * @var string
20
	 * @since 1.3
21
	 */
22
	protected $video_container_id;
23
24
	/**
25
	 * DOM identifier of the video element (video, object, embed)
26
	 *
27
	 * @var string
28
	 * @since 1.3
29
	 */
30
	protected $video_id;
31
32
	/**
33
	 * Array of playback options: force_flash or freedom
34
	 *
35
	 * @var array
36
	 * @since 1.3
37
	 */
38
	protected $options;
39
40
	/**
41
	 * Array of video GUIDs shown and their counts,
42
	 * moved from the old VideoPress class.
43
	 */
44
	public static $shown = array();
45
46
	/**
47
	 * Initiate a player object based on shortcode values and possible blog-level option overrides
48
	 *
49
	 * @since 1.3
50
	 * @var string $guid VideoPress unique identifier
51
	 * @var int $maxwidth maximum desired width of the video player if specified
52
	 * @var array $options player customizations
53
	 */
54
	public function __construct( $guid, $maxwidth = 0, $options = array() ) {
55
		if ( empty( self::$shown[ $guid ] ) ) {
56
			self::$shown[ $guid ] = 0;
57
		}
58
59
		self::$shown[ $guid ]++;
60
61
		$this->video_container_id = 'v-' . $guid . '-' . self::$shown[ $guid ];
62
		$this->video_id           = $this->video_container_id . '-video';
63
64
		if ( is_array( $options ) ) {
65
			$this->options = $options;
66
		} else {
67
			$this->options = array();
68
		}
69
70
		// set up the video
71
		$cache_key = null;
72
73
		// disable cache in debug mode
74
		if ( defined( 'WP_DEBUG' ) && WP_DEBUG === true ) {
75
			$cached_video = null;
76
		} else {
77
			$cache_key_pieces = array( 'video' );
78
79
			if ( is_multisite() && is_subdomain_install() ) {
80
				$cache_key_pieces[] = get_current_blog_id();
81
			}
82
83
			$cache_key_pieces[] = $guid;
84
			if ( $maxwidth > 0 ) {
85
				$cache_key_pieces[] = $maxwidth;
86
			}
87
			if ( is_ssl() ) {
88
				$cache_key_pieces[] = 'ssl';
89
			}
90
			$cache_key = implode( '-', $cache_key_pieces );
91
			unset( $cache_key_pieces );
92
			$cached_video = wp_cache_get( $cache_key, 'video' );
93
		}
94
		if ( empty( $cached_video ) ) {
95
			$video = new VideoPress_Video( $guid, $maxwidth );
96
			if ( empty( $video ) ) {
97
				return;
98
			} elseif ( isset( $video->error ) ) {
99
				$this->video = $video->error;
0 ignored issues
show
Bug introduced by
The property error does not seem to exist in VideoPress_Video.

An attempt at access to an undefined property has been detected. This may either be a typographical error or the property has been renamed but there are still references to its old name.

If you really want to allow access to undefined properties, you can define magic methods to allow access. See the php core documentation on Overloading.

Loading history...
100
				return;
101
			} elseif ( is_wp_error( $video ) ) {
102
				$this->video = $video;
103
				return;
104
			}
105
106
			$this->video = $video;
107
			unset( $video );
108
109
			if ( ! defined( 'WP_DEBUG' ) || WP_DEBUG !== true ) {
110
				$expire = 3600;
111
				if ( isset( $this->video->expires ) && is_int( $this->video->expires ) ) {
112
					$expires_diff = time() - $this->video->expires;
113
					if ( $expires_diff > 0 && $expires_diff < 86400 ) { // allowed range: 1 second to 1 day
114
						$expire = $expires_diff;
115
					}
116
					unset( $expires_diff );
117
				}
118
119
				wp_cache_set( $cache_key, serialize( $this->video ), 'video', $expire );
120
				unset( $expire );
121
			}
122
		} else {
123
			$this->video = unserialize( $cached_video );
124
		}
125
		unset( $cache_key );
126
		unset( $cached_video );
127
	}
128
129
	/**
130
	 * Wrap output in a VideoPress player container
131
	 *
132
	 * @since 1.3
133
	 * @var string $content HTML string
134
	 * @return string HTML string or blank string if nothing to wrap
135
	 */
136
	private function html_wrapper( $content ) {
137
		if ( empty( $content ) ) {
138
			return '';
139
		} else {
140
			return '<div id="' . esc_attr( $this->video_container_id ) . '" class="video-player">' . $content . '</div>';
141
		}
142
	}
143
144
	/**
145
	 * Output content suitable for a feed reader displaying RSS or Atom feeds
146
	 * We do not display error messages in the feed view due to caching concerns.
147
	 * Flash content presented using <embed> markup for feed reader compatibility.
148
	 *
149
	 * @since 1.3
150
	 * @return string HTML string or empty string if error
151
	 */
152
	public function asXML() {
153
		if ( empty( $this->video ) || is_wp_error( $this->video ) ) {
154
			return '';
155
		}
156
157
		if ( isset( $this->options['force_flash'] ) && true === $this->options['force_flash'] ) {
158
			$content = $this->flash_embed();
159
160
		} else {
161
			$content = $this->html5_static();
162
		}
163
164
		return $this->html_wrapper( $content );
165
	}
166
167
	/**
168
	 * Video player markup for best matching the current request and publisher options
169
	 *
170
	 * @since 1.3
171
	 * @return string HTML markup string or empty string if no video property found
172
	 */
173
	public function asHTML() {
174
		if ( empty( $this->video ) ) {
175
			$content = '';
176
177
		} elseif ( is_wp_error( $this->video ) ) {
178
			$content = $this->error_message( $this->video );
179
180
		} elseif ( isset( $this->options['force_flash'] ) && true === $this->options['force_flash'] ) {
181
			$content = $this->flash_object();
182
183
		} elseif ( isset( $this->video->restricted_embed ) && true === $this->video->restricted_embed ) {
184
185
			if ( $this->options['forcestatic'] ) {
186
				$content = $this->flash_object();
187
188
			} else {
189
				$content = $this->html5_dynamic();
190
			}
191
		} elseif ( isset( $this->options['freedom'] ) && true === $this->options['freedom'] ) {
192
			$content = $this->html5_static();
193
194
		} else {
195
			$content = $this->html5_dynamic();
196
		}
197
198
		return $this->html_wrapper( $content );
199
	}
200
201
	/**
202
	 * Display an error message to users capable of doing something about the error
203
	 *
204
	 * @since 1.3
205
	 * @uses current_user_can() to test if current user has edit_posts capability
206
	 * @var WP_Error $error WordPress error
207
	 * @return string HTML string
208
	 */
209
	private function error_message( $error ) {
210
		if ( ! current_user_can( 'edit_posts' ) || empty( $error ) ) {
211
			return '';
212
		}
213
214
		$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">';
215
		$html .= '<h1 style="font-size:180%;font-style:bold;line-height:130%;text-decoration:underline">' . esc_html( sprintf( __( '%s Error', 'jetpack' ), 'VideoPress' ) ) . '</h1>';
216
		foreach ( $error->get_error_messages() as $message ) {
0 ignored issues
show
Bug introduced by
The method get_error_messages() does not seem to exist on object<WP_Error>.

This check looks for calls to methods that do not seem to exist on a given type. It looks for the method on the type itself as well as in inherited classes or implemented interfaces.

This is most likely a typographical error or the method has been renamed.

Loading history...
217
			$html .= $message;
218
		}
219
		$html .= '</div>';
220
		return $html;
221
	}
222
223
	/**
224
	 * Rating agencies and industry associations require a potential viewer verify his or her age before a video or its poster frame are displayed.
225
	 * Content rated for audiences 17 years of age or older requires such verification across multiple rating agencies and industry associations
226
	 *
227
	 * @since 1.3
228
	 * @return bool true if video requires the viewer verify he or she is 17 years of age or older
229
	 */
230
	private function age_gate_required() {
231
		if ( isset( $this->video->age_rating ) && $this->video->age_rating >= 17 ) {
232
			return true;
233
		} else {
234
			return false;
235
		}
236
	}
237
238
	/**
239
	 * Select a date of birth using HTML form elements.
240
	 *
241
	 * @since 1.5
242
	 * @return string HTML markup
243
	 */
244
	private function html_age_gate() {
245
		global $wp_locale;
246
		$text_align = 'left';
247
		if ( $this->video->text_direction === 'rtl' ) {
248
			$text_align = 'right';
249
		}
250
251
		$html         = '<div class="videopress-age-gate" style="margin:0 60px">';
252
		$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>';
253
		$html        .= '<fieldset id="birthday" style="border:0 none;text-align:' . $text_align . ';padding:0;">';
254
		$inputs_style = 'border:1px solid #444;margin-';
255
		if ( $this->video->text_direction === 'rtl' ) {
256
			$inputs_style .= 'left';
257
		} else {
258
			$inputs_style .= 'right';
259
		}
260
		$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';
261
262
		/**
263
		 * Display a list of months in the Gregorian calendar.
264
		 * Set values to 0-based to match JavaScript Date.
265
		 *
266
		 * @link https://developer.mozilla.org/en/JavaScript/Reference/global_objects/date Mozilla JavaScript Reference: Date
267
		 */
268
		$html .= '<select name="month" style="' . $inputs_style . '">';
269
270
		for ( $i = 0; $i < 12; $i++ ) {
271
			$html .= '<option value="' . esc_attr( $i ) . '">' . esc_html( $wp_locale->get_month( $i + 1 ) ) . '</option>';
272
		}
273
		$html .= '</select>';
274
275
		/**
276
		 * todo: numdays variance by month
277
		 */
278
		$html .= '<select name="day" style="' . $inputs_style . '">';
279
		for ( $i = 1; $i < 32; $i++ ) {
280
			$html .= '<option>' . $i . '</option>';
281
		}
282
		$html .= '</select>';
283
284
		/**
285
		 * Current record for human life is 122. Go back 130 years and no one is left out.
286
		 * Don't ask infants younger than 2 for their birthday
287
		 * Default to 13
288
		 */
289
		$html        .= '<select name="year" style="' . $inputs_style . '">';
290
		$start_year   = date( 'Y' ) - 2;
291
		$default_year = $start_year - 11;
292
		$end_year     = $start_year - 128;
293
		for ( $year = $start_year; $year > $end_year; $year-- ) {
294
			$html .= '<option';
295
			if ( $year === $default_year ) {
296
				$html .= ' selected="selected"';
297
			}
298
			$html .= '>' . $year . '</option>';
299
		}
300
		unset( $start_year );
301
		unset( $default_year );
302
		unset( $end_year );
303
		$html .= '</select>';
304
305
		$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" />';
306
307
		$html .= '</fieldset>';
308
		$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>';
309
310
		$html .= '</div>';
311
		return $html;
312
	}
313
314
	/**
315
	 * Return HTML5 video static markup for the given video parameters.
316
	 * Use default browser player controls.
317
	 * No Flash fallback.
318
	 *
319
	 * @since 1.2
320
	 * @link https://html.spec.whatwg.org/multipage/media.html#the-video-element HTML5 video
321
	 * @return string HTML5 video element and children
322
	 */
323
	private function html5_static() {
324
		wp_enqueue_script( 'videopress' );
325
		$thumbnail = esc_url( $this->video->poster_frame_uri );
326
		$html      = "<video id=\"{$this->video_id}\" width=\"{$this->video->calculated_width}\" height=\"{$this->video->calculated_height}\" poster=\"$thumbnail\" controls=\"true\"";
327 View Code Duplication
		if ( isset( $this->options['autoplay'] ) && $this->options['autoplay'] === true ) {
328
			$html .= ' autoplay="true"';
329
		} else {
330
			$html .= ' preload="metadata"';
331
		}
332
		if ( isset( $this->video->text_direction ) ) {
333
			$html .= ' dir="' . esc_attr( $this->video->text_direction ) . '"';
334
		}
335 View Code Duplication
		if ( isset( $this->video->language ) ) {
336
			$html .= ' lang="' . esc_attr( $this->video->language ) . '"';
337
		}
338
		$html .= '>';
339
		if ( ! isset( $this->options['freedom'] ) || $this->options['freedom'] === false ) {
340
			$mp4 = $this->video->videos->mp4->url;
341 View Code Duplication
			if ( ! empty( $mp4 ) ) {
342
				$html .= '<source src="' . esc_url( $mp4 ) . '" type="video/mp4; codecs=&quot;' . esc_attr( $this->video->videos->mp4->codecs ) . '&quot;" />';
343
			}
344
			unset( $mp4 );
345
		}
346
347
		if ( isset( $this->video->videos->ogv ) ) {
348
			$ogg = $this->video->videos->ogv->url;
349 View Code Duplication
			if ( ! empty( $ogg ) ) {
350
				$html .= '<source src="' . esc_url( $ogg ) . '" type="video/ogg; codecs=&quot;' . esc_attr( $this->video->videos->ogv->codecs ) . '&quot;" />';
351
			}
352
353
			unset( $ogg );
354
		}
355
356
		$html .= '<div><img alt="';
357
		if ( isset( $this->video->title ) ) {
358
			$html .= esc_attr( $this->video->title );
359
		}
360
		$html .= '" src="' . $thumbnail . '" width="' . $this->video->calculated_width . '" height="' . $this->video->calculated_height . '" /></div>';
361
		if ( isset( $this->options['freedom'] ) && $this->options['freedom'] === true ) {
362
			$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>';
363
		} elseif ( isset( $this->video->title ) ) {
364
			$html .= '<p>' . esc_html( $this->video->title ) . '</p>';
365
		}
366
		$html .= '</video>';
367
		return $html;
368
	}
369
370
	/**
371
	 * Click to play dynamic HTML5-capable player.
372
	 * The player displays a video preview section including poster frame,
373
	 * video title, play button and watermark on the original page load
374
	 * and calculates the playback capabilities of the browser. The video player
375
	 * is loaded when the visitor clicks on the video preview area.
376
	 * If Flash Player 10 or above is available the browser will display
377
	 * the Flash version of the video. If HTML5 video appears to be supported
378
	 * and the browser may be capable of MP4 (H.264, AAC) or OGV (Theora, Vorbis)
379
	 * playback the browser will display its native HTML5 player.
380
	 *
381
	 * @since 1.5
382
	 * @return string HTML markup
383
	 */
384
	private function html5_dynamic() {
385
386
		/**
387
		 * Filter the VideoPress legacy player feature
388
		 *
389
		 * This filter allows you to control whether the legacy VideoPress player should be used
390
		 * instead of the improved one.
391
		 *
392
		 * @module videopress
393
		 *
394
		 * @since 3.7.0
395
		 *
396
		 * @param boolean $videopress_use_legacy_player
397
		 */
398
		if ( ! apply_filters( 'jetpack_videopress_use_legacy_player', false ) ) {
399
			return $this->html5_dynamic_next();
400
		}
401
402
		wp_enqueue_script( 'videopress' );
403
		$video_placeholder_id = $this->video_container_id . '-placeholder';
404
		$age_gate_required    = $this->age_gate_required();
405
		$width                = absint( $this->video->calculated_width );
406
		$height               = absint( $this->video->calculated_height );
407
408
		$html = '<div id="' . $video_placeholder_id . '" class="videopress-placeholder" style="';
409
		if ( $age_gate_required ) {
410
			$html .= "min-width:{$width}px;min-height:{$height}px";
411
		} else {
412
			$html .= "width:{$width}px;height:{$height}px";
413
		}
414
		$html .= ';display:none;cursor:pointer !important;position:relative;';
415
		if ( isset( $this->video->skin ) && isset( $this->video->skin->background_color ) ) {
416
			$html .= 'background-color:' . esc_attr( $this->video->skin->background_color ) . ';';
417
		}
418
		$html .= 'font-family: \'Helvetica Neue\',Arial,Helvetica,\'Nimbus Sans L\',sans-serif;font-weight:bold;font-size:18px">' . PHP_EOL;
419
420
		/**
421
		 * Do not display a poster frame, title, or any other content hints for mature content.
422
		 */
423
		if ( ! $age_gate_required ) {
424
			if ( ! empty( $this->video->title ) ) {
425
				$html .= '<div class="videopress-title" style="display:inline;position:absolute;margin:20px 20px 0 20px;padding:4px 8px;vertical-align:top;text-align:';
426
				if ( $this->video->text_direction === 'rtl' ) {
427
					$html .= 'right" dir="rtl"';
428
				} else {
429
					$html .= 'left" dir="ltr"';
430
				}
431 View Code Duplication
				if ( isset( $this->video->language ) ) {
432
					$html .= ' lang="' . esc_attr( $this->video->language ) . '"';
433
				}
434
				$html .= '><span style="padding:3px 0;line-height:1.5em;';
435
				if ( isset( $this->video->skin ) && isset( $this->video->skin->background_color ) ) {
436
					$html .= 'background-color:';
437
					if ( $this->video->skin->background_color === 'rgb(0,0,0)' ) {
438
						$html .= 'rgba(0,0,0,0.8)';
439
					} else {
440
						$html .= esc_attr( $this->video->skin->background_color );
441
					}
442
					$html .= ';';
443
				}
444
				$html .= 'color:rgb(255,255,255)">' . esc_html( $this->video->title ) . '</span></div>';
445
			}
446
			$html .= '<img class="videopress-poster" alt="';
447
			if ( ! empty( $this->video->title ) ) {
448
				$html .= esc_attr( $this->video->title ) . '" title="' . esc_attr( sprintf( _x( 'Watch: %s', 'watch a video title', 'jetpack' ), $this->video->title ) );
449
			}
450
			$html .= '" src="' . esc_url( $this->video->poster_frame_uri, array( 'http', 'https' ) ) . '" width="' . $width . '" height="' . $height . '" />' . PHP_EOL;
451
452
			// style a play button hovered over the poster frame
453
			$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;
454
455
			// watermark
456
			if ( isset( $this->video->skin ) && isset( $this->video->skin->watermark ) ) {
457
				$html .= '<div style="position:relative;margin-top:-40px;height:25px;margin-bottom:35px;';
458
				if ( $this->video->text_direction === 'rtl' ) {
459
					$html .= 'margin-left:20px;text-align:left;';
460
				} else {
461
					$html .= 'margin-right:20px;text-align:right;';
462
				}
463
				$html .= 'vertical-align:bottom;z-index:3">';
464
				$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"/>';
465
				$html .= '</div>' . PHP_EOL;
466
			}
467
		}
468
469
		$data = array(
470
			'blog'     => absint( $this->video->blog_id ),
471
			'post'     => absint( $this->video->post_id ),
472
			'duration' => absint( $this->video->duration ),
473
			'poster'   => esc_url_raw( $this->video->poster_frame_uri, array( 'http', 'https' ) ),
474
			'hd'       => (bool) $this->options['hd'],
475
		);
476
		if ( isset( $this->video->videos ) ) {
477
			if ( isset( $this->video->videos->mp4 ) && isset( $this->video->videos->mp4->url ) ) {
478
				$data['mp4'] = array(
479
					'size' => $this->video->videos->mp4->format,
480
					'uri'  => esc_url_raw( $this->video->videos->mp4->url, array( 'http', 'https' ) ),
481
				);
482
			}
483
			if ( isset( $this->video->videos->ogv ) && isset( $this->video->videos->ogv->url ) ) {
484
				$data['ogv'] = array(
485
					'size' => 'std',
486
					'uri'  => esc_url_raw( $this->video->videos->ogv->url, array( 'http', 'https' ) ),
487
				);
488
			}
489
		}
490
		$locale = array( 'dir' => $this->video->text_direction );
491
		if ( isset( $this->video->language ) ) {
492
			$locale['lang'] = $this->video->language;
493
		}
494
		$data['locale'] = $locale;
495
		unset( $locale );
496
497
		$guid    = $this->video->guid;
498
		$guid_js = json_encode( $guid );
499
		$html   .= '<script type="text/javascript">' . PHP_EOL;
500
		$html   .= 'jQuery(document).ready(function() {';
501
502
		$html .= 'if ( !jQuery.VideoPress.data[' . json_encode( $guid ) . '] ) { jQuery.VideoPress.data[' . json_encode( $guid ) . '] = new Array(); }' . PHP_EOL;
503
		$html .= 'jQuery.VideoPress.data[' . json_encode( $guid ) . '][' . self::$shown[ $guid ] . ']=' . json_encode( $data ) . ';' . PHP_EOL;
504
		unset( $data );
505
506
		$jq_container   = json_encode( '#' . $this->video_container_id );
507
		$jq_placeholder = json_encode( '#' . $video_placeholder_id );
508
		$player_config  = "{width:{$width},height:{$height},";
509
		if ( isset( $this->options['freedom'] ) && $this->options['freedom'] === true ) {
510
			$player_config .= 'freedom:"true",';
511
		}
512
		$player_config .= 'container:jQuery(' . $jq_container . ')}';
513
514
		$html .= "jQuery({$jq_placeholder}).show(0,function(){jQuery.VideoPress.analytics.impression({$guid_js})});" . PHP_EOL;
515
516
		if ( $age_gate_required ) {
517
			$html .= 'if ( jQuery.VideoPress.support.flash() ) {' . PHP_EOL;
518
			/**
519
			 * @link https://github.com/swfobject/swfobject/wiki/SWFObject-API#swfobjectembedswfswfurlstr-replaceelemidstr-widthstr-heightstr-swfversionstr-xiswfurlstr-flashvarsobj-parobj-attobj-callbackfn
520
			 */
521
			$html .= 'swfobject.embedSWF(' . implode(
522
				',',
523
				array(
524
					'jQuery.VideoPress.video.flash.player_uri',
525
					json_encode( $this->video_container_id ),
526
					json_encode( $width ),
527
					json_encode( $height ),
528
					'jQuery.VideoPress.video.flash.min_version',
529
					'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
530
					'{guid:' . $guid_js . '}', // FlashVars
531
					'jQuery.VideoPress.video.flash.params',
532
					'null', // no attributes
533
					'jQuery.VideoPress.video.flash.embedCallback', // error fallback
534
				)
535
			) . ');';
536
			$html .= '} else {' . PHP_EOL;
537
			$html .= "if ( jQuery.VideoPress.video.prepare({$guid_js},{$player_config}," . self::$shown[ $guid ] . ') ) {' . PHP_EOL;
538
			$html .= 'if ( jQuery(' . $jq_container . ').data( "player" ) === "flash" ){jQuery.VideoPress.video.play(jQuery(' . json_encode( '#' . $this->video_container_id ) . '));}else{';
539
			$html .= 'jQuery(' . $jq_placeholder . ').html(' . json_encode( $this->html_age_date() ) . ');' . PHP_EOL;
0 ignored issues
show
Bug introduced by
The method html_age_date() does not exist on VideoPress_Player. Did you maybe mean html_age_gate()?

This check marks calls to methods that do not seem to exist on an object.

This is most likely the result of a method being renamed without all references to it being renamed likewise.

Loading history...
540
			$html .= 'jQuery(' . 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;
541
			$html .= '}}}' . PHP_EOL;
542
		} else {
543
			$html .= "if ( jQuery.VideoPress.video.prepare({$guid_js}, {$player_config}," . self::$shown[ $guid ] . ') ) {' . PHP_EOL;
544
			if ( isset( $this->options['autoplay'] ) && $this->options['autoplay'] === true ) {
545
				$html .= "jQuery.VideoPress.video.play(jQuery({$jq_container}));";
546
			} else {
547
				$html .= 'jQuery(' . $jq_placeholder . ').one("click",function(){jQuery.VideoPress.video.play(jQuery(' . $jq_container . '))});';
548
			}
549
			$html .= '}';
550
551
			// close the jQuery(document).ready() function
552
			$html .= '});';
553
		}
554
		$html .= '</script>' . PHP_EOL;
555
		$html .= '</div>' . PHP_EOL;
556
557
		/*
558
		 * JavaScript required
559
		 */
560
		$noun = __( 'this video', 'jetpack' );
561
		if ( ! $age_gate_required ) {
562
			$vid_type = '';
563
			if ( ( isset( $this->options['freedom'] ) && $this->options['freedom'] === true ) && ( isset( $this->video->videos->ogv ) && isset( $this->video->videos->ogv->url ) ) ) {
564
				$vid_type = 'ogv';
565
			} elseif ( isset( $this->video->videos->mp4 ) && isset( $this->video->videos->mp4->url ) ) {
566
				$vid_type = 'mp4';
567
			} elseif ( isset( $this->video->videos->ogv ) && isset( $this->video->videos->ogv->url ) ) {
568
				$vid_type = 'ogv';
569
			}
570
571
			if ( $vid_type !== '' ) {
572
				$noun = '<a ';
573 View Code Duplication
				if ( isset( $this->video->language ) ) {
574
					$noun .= 'hreflang="' . esc_attr( $this->video->language ) . '" ';
575
				}
576
				if ( $vid_type === 'mp4' ) {
577
					$noun .= 'type="video/mp4" href="' . esc_url( $this->video->videos->mp4->url, array( 'http', 'https' ) );
578
				} elseif ( $vid_type === 'ogv' ) {
579
					$noun .= 'type="video/ogv" href="' . esc_url( $this->video->videos->ogv->url, array( 'http', 'https' ) );
580
				}
581
				$noun .= '">';
582
				if ( isset( $this->video->title ) ) {
583
					$noun .= esc_html( $this->video->title );
584
				} else {
585
					$noun .= __( 'this video', 'jetpack' );
586
				}
587
				$noun .= '</a>';
588
			} elseif ( ! empty( $this->title ) ) {
0 ignored issues
show
Bug introduced by
The property title does not exist. Did you maybe forget to declare it?

In PHP it is possible to write to properties without declaring them. For example, the following is perfectly valid PHP code:

class MyClass { }

$x = new MyClass();
$x->foo = true;

Generally, it is a good practice to explictly declare properties to avoid accidental typos and provide IDE auto-completion:

class MyClass {
    public $foo;
}

$x = new MyClass();
$x->foo = true;
Loading history...
589
				$noun = esc_html( $this->title );
590
			}
591
			unset( $vid_type );
592
		}
593
		$html .= '<noscript><p>' . sprintf( _x( 'JavaScript required to play %s.', 'Play as in playback or view a movie', 'jetpack' ), $noun ) . '</p></noscript>';
594
595
		return $html;
596
	}
597
598
	function html5_dynamic_next() {
599
		$video_container_id = 'v-' . $this->video->guid;
600
601
		// Must not use iframes for IE11 due to a fullscreen bug
602
		if ( isset( $_SERVER['HTTP_USER_AGENT'] ) && stristr( $_SERVER['HTTP_USER_AGENT'], 'Trident/7.0; rv:11.0' ) ) {
603
			$iframe_embed = false;
604
		} else {
605
606
			/**
607
			 * Filter the VideoPress iframe embed
608
			 *
609
			 * This filter allows you to control whether the videos will be embedded using an iframe.
610
			 * Set this to false in order to use an in-page embed rather than an iframe.
611
			 *
612
			 * @module videopress
613
			 *
614
			 * @since 3.7.0
615
			 *
616
			 * @param boolean $videopress_player_use_iframe
617
			 */
618
			$iframe_embed = apply_filters( 'jetpack_videopress_player_use_iframe', true );
619
		}
620
621
		if ( ! array_key_exists( 'hd', $this->options ) ) {
622
			$this->options['hd'] = (bool) get_option( 'video_player_high_quality', false );
623
		}
624
625
		$videopress_options = array(
626
			'width'  => absint( $this->video->calculated_width ),
627
			'height' => absint( $this->video->calculated_height ),
628
		);
629
		foreach ( $this->options as $option => $value ) {
630
			switch ( $option ) {
631
				case 'at':
632
					if ( (int) $value ) {
633
						$videopress_options[ $option ] = (int) $value;
634
					}
635
					break;
636
				case 'autoplay':
637
					$option = 'autoPlay';
638
				case 'hd':
639
				case 'loop':
640
				case 'permalink':
641
					if ( in_array( $value, array( 1, 'true' ) ) ) {
642
						$videopress_options[ $option ] = true;
643
					} elseif ( in_array( $value, array( 0, 'false' ) ) ) {
644
						$videopress_options[ $option ] = false;
645
					}
646
					break;
647
				case 'defaultlangcode':
648
					$option = 'defaultLangCode';
649
					if ( $value ) {
650
						$videopress_options[ $option ] = $value;
651
					}
652
					break;
653
			}
654
		}
655
656
		if ( $iframe_embed ) {
657
			$iframe_url = "https://videopress.com/embed/{$this->video->guid}";
658
659
			foreach ( $videopress_options as $option => $value ) {
660
				if ( ! in_array( $option, array( 'width', 'height' ) ) ) {
661
662
					// add_query_arg ignores false as a value, so replacing it with 0
663
					$iframe_url = add_query_arg( $option, ( false === $value ) ? 0 : $value, $iframe_url );
664
				}
665
			}
666
667
			$js_url = 'https://s0.wp.com/wp-content/plugins/video/assets/js/next/videopress-iframe.js';
668
669
			return "<iframe width='" . esc_attr( $videopress_options['width'] )
670
				. "' height='" . esc_attr( $videopress_options['height'] )
671
				. "' src='" . esc_attr( $iframe_url )
672
				. "' frameborder='0' allowfullscreen></iframe>"
673
				. "<script src='" . esc_attr( $js_url ) . "'></script>";
674
675
		} else {
676
			$videopress_options = json_encode( $videopress_options );
677
			$js_url             = 'https://s0.wp.com/wp-content/plugins/video/assets/js/next/videopress.js';
678
679
			return "<div id='{$video_container_id}'></div>
680
				<script src='{$js_url}'></script>
681
				<script>
682
					videopress('{$this->video->guid}', document.querySelector('#{$video_container_id}'), {$videopress_options});
683
				</script>";
684
		}
685
	}
686
687
	/**
688
	 * Only allow legitimate Flash parameters and their values
689
	 *
690
	 * @since 1.2
691
	 * @link https://helpx.adobe.com/flash/kb/flash-object-embed-tag-attributes.html Flash object and embed attributes
692
	 * @link https://helpx.adobe.com/flash/kb/font-outlines-device-fonts.html devicefont
693
	 * @link https://helpx.adobe.com/flash/kb/control-access-scripts-host-web.html allowscriptaccess
694
	 * @link https://www.adobe.com/devnet/flashplayer/articles/full_screen_mode.html full screen mode
695
	 * @link https://help.adobe.com/en_US/as3/dev/WS1EFE2EDA-026D-4d14-864E-79DFD56F87C6.html allownetworking
696
	 * @param array $flash_params Flash parameters expressed in key-value form
697
	 * @return array validated Flash parameters
698
	 */
699
	public static function esc_flash_params( $flash_params ) {
700
		$allowed_params = array(
701
			'swliveconnect'         => array( 'true', 'false' ),
702
			'play'                  => array( 'true', 'false' ),
703
			'loop'                  => array( 'true', 'false' ),
704
			'menu'                  => array( 'true', 'false' ),
705
			'quality'               => array( 'low', 'autolow', 'autohigh', 'medium', 'high', 'best' ),
706
			'scale'                 => array( 'default', 'noborder', 'exactfit', 'noscale' ),
707
			'align'                 => array( 'l', 'r', 't' ),
708
			'salign'                => array( 'l', 'r', 't', 'tl', 'tr', 'bl', 'br' ),
709
			'wmode'                 => array( 'window', 'opaque', 'transparent', 'direct', 'gpu' ),
710
			'devicefont'            => array( '_sans', '_serif', '_typewriter' ),
711
			'allowscriptaccess'     => array( 'always', 'samedomain', 'never' ),
712
			'allownetworking'       => array( 'all', 'internal', 'none' ),
713
			'seamlesstabbing'       => array( 'true', 'false' ),
714
			'allowfullscreen'       => array( 'true', 'false' ),
715
			'fullScreenAspectRatio' => array( 'portrait', 'landscape' ),
716
			'base',
717
			'bgcolor',
718
			'flashvars',
719
		);
720
721
		$allowed_params_keys = array_keys( $allowed_params );
722
723
		$filtered_params = array();
724
		foreach ( $flash_params as $param => $value ) {
725
			if ( empty( $param ) || empty( $value ) ) {
726
				continue;
727
			}
728
			$param = strtolower( $param );
729
			if ( in_array( $param, $allowed_params_keys ) ) {
730
				if ( isset( $allowed_params[ $param ] ) && is_array( $allowed_params[ $param ] ) ) {
731
					$value = strtolower( $value );
732
					if ( in_array( $value, $allowed_params[ $param ] ) ) {
733
						$filtered_params[ $param ] = $value;
734
					}
735
				} else {
736
					$filtered_params[ $param ] = $value;
737
				}
738
			}
739
		}
740
		unset( $allowed_params_keys );
741
742
		/**
743
		 * Flash specifies sameDomain, not samedomain. change from lowercase value for preciseness
744
		 */
745
		if ( isset( $filtered_params['allowscriptaccess'] ) && $filtered_params['allowscriptaccess'] === 'samedomain' ) {
746
			$filtered_params['allowscriptaccess'] = 'sameDomain';
747
		}
748
749
		return $filtered_params;
750
	}
751
752
	/**
753
	 * Filter Flash variables from the response, taking into consideration player options.
754
	 *
755
	 * @since 1.3
756
	 * @return array Flash variable key value pairs
757
	 */
758
	private function get_flash_variables() {
759
		if ( ! isset( $this->video->players->swf->vars ) ) {
760
			return array();
761
		}
762
763
		$flashvars = (array) $this->video->players->swf->vars;
764 View Code Duplication
		if ( isset( $this->options['autoplay'] ) && $this->options['autoplay'] === true ) {
765
			$flashvars['autoPlay'] = 'true';
766
		}
767
		return $flashvars;
768
	}
769
770
	/**
771
	 * Validate and filter Flash parameters
772
	 *
773
	 * @since 1.3
774
	 * @return array Flash parameters passed through key and value validation
775
	 */
776
	private function get_flash_parameters() {
777
		if ( ! isset( $this->video->players->swf->params ) ) {
778
			return array();
779
		} else {
780
			return self::esc_flash_params(
781
				/**
782
						 * Filters the Flash parameters of the VideoPress player.
783
						 *
784
						 * @module videopress
785
						 *
786
						 * @since 1.2.0
787
						 *
788
						 * @param array $this->video->players->swf->params Array of swf parameters for the VideoPress flash player.
789
						 */
790
				apply_filters( 'video_flash_params', (array) $this->video->players->swf->params, 10, 1 )
0 ignored issues
show
Unused Code introduced by
The call to apply_filters() has too many arguments starting with 10.

This check compares calls to functions or methods with their respective definitions. If the call has more arguments than are defined, it raises an issue.

If a function is defined several times with a different number of parameters, the check may pick up the wrong definition and report false positives. One codebase where this has been known to happen is Wordpress.

In this case you can add the @ignore PhpDoc annotation to the duplicate definition and it will be ignored.

Loading history...
791
			);
792
		}
793
	}
794
795
	/**
796
	 * Flash player markup in a HTML embed element.
797
	 *
798
	 * @since 1.1
799
	 * @link https://html.spec.whatwg.org/multipage/iframe-embed-object.html#the-embed-element embed element
800
	 * @link http://www.google.com/support/reader/bin/answer.py?answer=70664 Google Reader markup support
801
	 * @return string HTML markup. Embed element with no children
802
	 */
803
	private function flash_embed() {
804
		wp_enqueue_script( 'videopress' );
805 View Code Duplication
		if ( ! isset( $this->video->players->swf ) || ! isset( $this->video->players->swf->url ) ) {
806
			return '';
807
		}
808
809
		$embed = array(
810
			'id'     => $this->video_id,
811
			'src'    => esc_url_raw( $this->video->players->swf->url . '&' . http_build_query( $this->get_flash_variables(), null, '&' ), array( 'http', 'https' ) ),
812
			'type'   => 'application/x-shockwave-flash',
813
			'width'  => $this->video->calculated_width,
814
			'height' => $this->video->calculated_height,
815
		);
816
		if ( isset( $this->video->title ) ) {
817
			$embed['title'] = $this->video->title;
818
		}
819
		$embed = array_merge( $embed, $this->get_flash_parameters() );
820
821
		$html = '<embed';
822
		foreach ( $embed as $attribute => $value ) {
823
			$html .= ' ' . esc_html( $attribute ) . '="' . esc_attr( $value ) . '"';
824
		}
825
		unset( $embed );
826
		$html .= '></embed>';
827
		return $html;
828
	}
829
830
	/**
831
	 * Double-baked Flash object markup for Internet Explorer and more standards-friendly consuming agents.
832
	 *
833
	 * @since 1.1
834
	 * @return HTML markup. Object and children.
835
	 */
836
	private function flash_object() {
837
		wp_enqueue_script( 'videopress' );
838 View Code Duplication
		if ( ! isset( $this->video->players->swf ) || ! isset( $this->video->players->swf->url ) ) {
839
			return '';
840
		}
841
842
		$thumbnail_html = '<img alt="';
843
		if ( isset( $this->video->title ) ) {
844
			$thumbnail_html .= esc_attr( $this->video->title );
845
		}
846
		$thumbnail_html .= '" src="' . esc_url( $this->video->poster_frame_uri, array( 'http', 'https' ) ) . '" width="' . $this->video->calculated_width . '" height="' . $this->video->calculated_height . '" />';
847
		$flash_vars      = esc_attr( http_build_query( $this->get_flash_variables(), null, '&' ) );
848
		$flash_params    = '';
849
		foreach ( $this->get_flash_parameters() as $attribute => $value ) {
850
			$flash_params .= '<param name="' . esc_attr( $attribute ) . '" value="' . esc_attr( $value ) . '" />';
851
		}
852
		$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/' );
853
		$flash_player_url = esc_url( $this->video->players->swf->url, array( 'http', 'https' ) );
854
		$description      = '';
855
		if ( isset( $this->video->title ) ) {
856
			$standby     = $this->video->title;
857
			$description = '<p><strong>' . esc_html( $this->video->title ) . '</strong></p>';
858
		} else {
859
			$standby = __( 'Loading video...', 'jetpack' );
860
		}
861
		$standby = ' standby="' . esc_attr( $standby ) . '"';
862
		return <<<OBJECT
863
<script type="text/javascript">if(typeof swfobject!=="undefined"){swfobject.registerObject("{$this->video_id}", "{$this->video->players->swf->version}");}</script>
864
<object classid="clsid:D27CDB6E-AE6D-11cf-96B8-444553540000" width="{$this->video->calculated_width}" height="{$this->video->calculated_height}" id="{$this->video_id}"{$standby}>
865
	<param name="movie" value="{$flash_player_url}" />
866
	{$flash_params}
867
	<param name="flashvars" value="{$flash_vars}" />
868
	<!--[if !IE]>-->
869
	<object type="application/x-shockwave-flash" data="{$flash_player_url}" width="{$this->video->calculated_width}" height="{$this->video->calculated_height}"{$standby}>
870
		{$flash_params}
871
		<param name="flashvars" value="{$flash_vars}" />
872
	<!--<![endif]-->
873
	{$thumbnail_html}{$description}<p class="robots-nocontent">{$flash_help}</p>
874
	<!--[if !IE]>-->
875
	</object>
876
	<!--<![endif]-->
877
</object>
878
OBJECT;
879
	}
880
}
881