PluginProbe ʕ •ᴥ•ʔ
Presto Player / 2.3.3
Presto Player v2.3.3
4.3.0 4.2.4 4.2.3 4.2.2 4.2.0 4.2.1 trunk 1.10.0 1.10.1 1.10.2 1.11.0 1.12.0 1.13.0 1.14.0 1.14.1 1.5.10 1.5.11 1.5.12 1.5.13 1.5.14 1.5.15 1.5.5 1.5.6 1.5.7 1.5.8 1.5.9 1.6.0 1.6.1 1.6.10 1.6.11 1.6.12 1.6.13 1.6.2 1.6.3 1.6.4 1.6.5 1.6.6 1.6.7 1.6.8 1.6.9 1.7.0 1.7.1 1.7.2 1.8.0 1.8.1 1.8.2 1.8.3 1.8.4 1.8.5 1.8.6 1.9.0 1.9.1 1.9.10 1.9.11 1.9.12 1.9.13 1.9.14 1.9.2 1.9.3 1.9.4 1.9.5 1.9.6 1.9.7 1.9.8 1.9.9 2.0.0 2.0.1 2.0.10 2.0.11 2.0.12 2.0.13 2.0.14 2.0.15 2.0.16 2.0.2 2.0.3 2.0.4 2.0.5 2.0.6 2.0.7 2.0.8 2.0.9 2.1.0 2.2.0 2.2.1 2.2.2 2.2.3 2.2.3-beta1 2.3.0 2.3.1 2.3.2 2.3.3 3.0.0 3.0.0-beta1 3.0.1 3.0.2 3.0.3 3.0.4 3.0.5 3.0.6 3.0.7 3.0.8 3.1.0 3.1.1 3.1.2 3.1.3 4.0.0 4.0.1 4.0.2 4.0.3 4.0.4 4.0.5 4.0.6 4.0.7 4.0.8 4.1.0 4.1.1 4.1.2 4.1.3 4.1.4
presto-player / inc / Support / Block.php
presto-player / inc / Support Last commit date
Block.php 2 years ago BlockFinder.php 5 years ago DynamicData.php 4 years ago HasOneRelationship.php 5 years ago Integration.php 5 years ago Utility.php 4 years ago
Block.php
716 lines
1 <?php
2
3 namespace PrestoPlayer\Support;
4
5
6 use PrestoPlayer\Plugin;
7 use PrestoPlayer\Models\Video;
8 use PrestoPlayer\Models\Player;
9 use PrestoPlayer\Models\Preset;
10 use PrestoPlayer\Models\AudioPreset;
11 use PrestoPlayer\Models\Setting;
12 use PrestoPlayer\WPackio\Enqueue;
13 use PrestoPlayer\Support\DynamicData;
14 use PrestoPlayer\Integrations\LearnDash\LearnDash;
15
16 use function cli\err;
17
18 class Block
19 {
20 protected $enqueue;
21 protected $assets;
22 protected $video_assets;
23 protected $name = '';
24 protected $template_name = 'video';
25 public $services;
26
27 /**
28 * Attributes
29 *
30 * @var array
31 */
32 protected $attributes = [
33 'color' => [
34 'type' => 'string',
35 'default' => '#00b3ff',
36 ],
37 'blockAlignment' => [
38 'type' => 'string',
39 ],
40 'autoplay' => [
41 'type' => 'boolean'
42 ],
43 'id' => [
44 'type' => 'number',
45 ],
46 'src' => [
47 'type' => 'string'
48 ],
49 'imageID' => [
50 'type' => 'number',
51 ],
52 'poster' => [
53 'type' => 'string',
54 ],
55 'content' => [
56 'type' => 'boolean',
57 ],
58 'pip' => [
59 'type' => 'boolean',
60 'default' => true
61 ],
62 'fullscreen' => [
63 'type' => 'boolean',
64 'default' => true
65 ],
66 'captions' => [
67 'type' => 'boolean',
68 'default' => false
69 ],
70 'hideControls' => [
71 'type' => 'boolean',
72 'default' => true
73 ],
74 'playLarge' => [
75 'type' => 'boolean',
76 'default' => true
77 ],
78 'chapters' => [
79 'type' => 'array',
80 'default' => []
81 ],
82 'overlays' => [
83 'type' => 'array',
84 'default' => []
85 ],
86 'speed' => [
87 'type' => 'boolean',
88 'default' => true
89 ],
90 ];
91
92 /**
93 * Attributes to pass to web component
94 */
95 protected $component_attributes = [
96 'preset',
97 'chapters',
98 'overlays',
99 'tracks',
100 'branding',
101 'blockAttributes',
102 'config',
103 'skin',
104 'analytics',
105 'automations',
106 'provider',
107 'video_id',
108 'videoAttributes',
109 'audioAttributes',
110 'provider_video_id',
111 'youtube'
112 ];
113
114 /**
115 * Default attributes for the block.
116 *
117 * @var array
118 */
119 protected $default_attributes = [
120 'playsInline' => true
121 ];
122
123 public function __construct(bool $isPremium = false, $version = 1)
124 {
125 do_action('presto_player_before_block_output', [$this, 'middleware']);
126 }
127
128 /**
129 * Register the block type
130 *
131 * @return void
132 */
133 public function register()
134 {
135 $this->registerBlockType();
136 }
137
138 public function additionalAttributes()
139 {
140 return [];
141 }
142
143 /**
144 * Register dynamic block type
145 *
146 * @return void
147 */
148 public function registerBlockType()
149 {
150 register_block_type(
151 "presto-player/$this->name",
152 [
153 'attributes' => wp_parse_args($this->additionalAttributes(), $this->attributes),
154 'render_callback' => [$this, 'html'],
155 ]
156 );
157 }
158
159 /**
160 * Middleware to run before outputting template
161 * Should the block load?
162 *
163 * @param array $attributes
164 * @param string $content
165 * @return boolean
166 */
167 public function middleware($attributes, $content)
168 {
169 return true;
170 }
171
172 /**
173 * Sanitize attributes function
174 * Let's a parent class sanitize attributes before displaying
175 *
176 * @param array $attributes
177 * @param array $default_config
178 * @return array
179 */
180 public function sanitizeAttributes($attributes, $default_config)
181 {
182 return [];
183 }
184
185 /**
186 * Allow overriding attributes
187 *
188 * @param array $attributes
189 * @return array
190 */
191 public function overrideAttributes($attributes)
192 {
193 return apply_filters("presto_video_block_attributes_override", $attributes, $this);
194 }
195
196 /**
197 * Must sanitize attributes
198 *
199 * @param array $attributes
200 * @return array
201 */
202 private function _sanitizeAttibutes($attributes)
203 {
204
205 // attribute overrides
206 $attributes = $this->overrideAttributes($attributes);
207
208 // Apply default attributes if not set.
209 $attributes = $this->appyAttributeDefaults($attributes);
210
211 // video id
212 $id = !empty($attributes['id']) ? $attributes['id'] : 0;
213
214 if ('audio' === $this->name) {
215 $preset = $this->getAudioPreset(!empty($attributes['preset']) ? $attributes['preset'] : 0);
216 $preset->type = 'audio';
217 } else {
218 $preset = $this->getPreset(!empty($attributes['preset']) ? $attributes['preset'] : 0);
219 }
220 $branding = $this->getBranding($preset);
221 $class = $this->getClasses($attributes);
222 $playerClass = $this->getPlayerClasses($id, $preset);
223 $styles = $this->getPlayerStyles($preset, $branding);
224 $css = $this->getCSS($id);
225 $src = !empty($attributes['src']) ? $attributes['src'] : '';
226
227 // use title or source.
228 if (empty($attributes['title'])) {
229 $video = $id ? (new Video($id)) : false;
230 $attributes['title'] = $video ? $video->title : $src;
231 }
232
233 // Default config
234 $default_config = apply_filters(
235 'presto_player/block/default_attributes', [
236 'type' => $this->name,
237 'css' => wp_kses_post($css),
238 'class' => $class,
239 'is_hls' => $this->isHls($src),
240 'styles' => $styles,
241 'skin' => $preset->skin,
242 'playerClass' => $playerClass,
243 'id' => $id,
244 'src' => $src,
245 'autoplay' => !empty($attributes['autoplay']),
246 'playsInline' => !empty($attributes['playsInline']),
247 'poster' => !empty($attributes['poster']) ? $attributes['poster'] : '',
248 'branding' => $branding,
249 'youtube' => [
250 'noCookie' => (bool) Setting::get('youtube', 'nocookie'),
251 'channelId' => sanitize_text_field(Setting::get('youtube', 'channel_id')),
252 'show_count' => !empty($preset->action_bar['show_count'])
253 ],
254 'preload' => !empty($attributes['preload']) ? $attributes['preload'] : '',
255 'tracks' => !empty($attributes['tracks']) ? (array) $attributes['tracks'] : [],
256 'preset' => $preset ? $preset->toArray() : [],
257 'chapters' => !empty($attributes['chapters']) ? $attributes['chapters'] : [],
258 'overlays' => DynamicData::replaceItems(!empty($attributes['overlays']) ? $attributes['overlays'] : [], 'text'),
259 'blockAttributes' => $attributes,
260 'provider' => $this->name,
261 'analytics' => Setting::get('analytics', 'enable', false),
262 'automations' => Setting::get('performance', 'automations', true),
263 'title' => !empty($attributes['title']) ? html_entity_decode($attributes['title']) : '',
264 ], $attributes
265 );
266
267 return wp_parse_args(
268 $this->sanitizeAttributes($attributes, $default_config),
269 $default_config
270 );
271 }
272
273 /**
274 * Get CSS from settings
275 * Is it an HLS playlist
276 *
277 * @param string $src
278 * @return boolean
279 */
280 public function isHls($src)
281 {
282 $src = !empty($src) ? $src : '';
283 return \strpos($src, '.m3u8') !== false;
284 }
285
286 /**
287 * Get CSS from settings
288 * Validates before output
289 *
290 * @param integer $id
291 * @return string
292 */
293 public function getCSS($id)
294 {
295 return apply_filters(
296 'presto_player/player/css',
297 Utility::sanitizeCSS(
298 Setting::get('branding', 'player_css'),
299 $id
300 )
301 );
302 }
303
304 /**
305 * Gets the preset
306 *
307 * @param integer $id Preset ID
308 * @return \PrestoPlayer\Models\Preset
309 */
310 public function getPreset($id)
311 {
312 $preset = new Preset(!empty($id) ? $id : 0);
313 $preset_id = $preset->id;
314
315 if (empty($preset_id)) {
316 $preset = $preset->findWhere(['slug' => 'default']);
317 }
318
319 // replace watermark text.
320 if (!empty($preset->watermark['enabled'])) {
321 $watermark_text = [
322 'text' => DynamicData::replaceText($preset->watermark['text'])
323 ];
324
325 $preset->watermark = wp_parse_args($watermark_text, $preset->watermark);
326 }
327
328 return apply_filters('presto_player/presto_player_presets/data', $preset, 'video');
329 }
330
331 /**
332 * Gets the audio preset
333 *
334 * @param integer $id Preset ID
335 * @return \PrestoPlayer\Models\AudioPreset
336 */
337 public function getAudioPreset($id)
338 {
339 $preset = new AudioPreset(!empty($id) ? $id : 0);
340 $preset_id = $preset->id;
341
342 if (empty($preset_id)) {
343 $preset = $preset->findWhere(['slug' => 'default']);
344 }
345
346 return apply_filters('presto_player/presto_player_presets/data', $preset, 'audio');
347 }
348
349 /**
350 * Get player branding
351 *
352 * @param \PrestoPlayer\Models\Preset $preset
353 * @return array
354 */
355 public function getBranding($preset)
356 {
357 $branding = Player::getBranding();
358
359 // sanitize with sensible defaults
360 $branding['color'] = !empty($branding['color']) ? sanitize_hex_color($branding['color']) : 'rgba(43,51,63,.7)';
361 $branding['logo_width'] = !empty($branding['logo_width']) ? $branding['logo_width'] : 150;
362 $branding['logo'] = !empty($branding['logo']) && !$preset->hide_logo ? $branding['logo'] : '';
363
364 return $branding;
365 }
366
367 /**
368 * Get block classes
369 *
370 * @param array $attributes
371 * @return string
372 */
373 public function getClasses($attributes)
374 {
375 $block_alignment = isset($attributes['align']) ? sanitize_text_field($attributes['align']) : '';
376 return !empty($block_alignment) ? 'align' . $block_alignment : '';
377 }
378
379 /**
380 * Get player classes
381 *
382 * @param integer $id
383 * @param \PrestoPlayer\Models\Preset $preset
384 * @return string
385 */
386 public function getPlayerClasses($id, $preset)
387 {
388 $skin = $preset->skin;
389 $playerClass = 'presto-video-id-' . (int) $id;
390 $playerClass .= ' presto-preset-id-' . (int) $preset->id;
391
392 if (!empty($skin)) {
393 $playerClass .= ' skin-' . sanitize_text_field($skin);
394 }
395
396 $caption_style = $preset->caption_style;
397 if (!empty($caption_style)) {
398 $playerClass .= ' caption-style-' . sanitize_html_class($caption_style);
399 }
400
401 if (!empty($attributes['className'])) {
402 $playerClass .= ' ' . (string) $attributes['className'];
403 }
404
405 return $playerClass;
406 }
407
408 /**
409 * Get player styles
410 *
411 * @param \PrestoPlayer\Models\Preset $preset
412 * @param array $branding
413 * @return string
414 */
415 public function getPlayerStyles($preset, $branding)
416 {
417
418 // Set brand color.
419 $background_color = ( !empty($preset->background_color) ? sanitize_hex_color($preset->background_color) : "var(--presto-player-highlight-color, " . sanitize_hex_color($branding['color']) . ")" );
420 $styles = '--plyr-color-main: ' . $background_color . '; ';
421
422 // video
423 if ($preset->caption_background) {
424 $styles .= '--plyr-captions-background: ' . sanitize_hex_color($preset->caption_background) . '; ';
425 }
426 if ($preset->border_radius) {
427 $styles .= '--presto-player-border-radius: ' . (int) $preset->border_radius . 'px; ';
428 }
429
430 if ($branding['logo_width']) {
431 $styles .= '--presto-player-logo-width: ' . (int) $branding['logo_width'] . 'px; ';
432 }
433 if (!empty($preset->email_collection['border_radius'])) {
434 $styles .= '--presto-player-email-border-radius: ' . (int) $preset->email_collection['border_radius'] . 'px; ';
435 }
436
437 // audio
438 if ($preset->type === 'audio') {
439 if ($preset->background_color) {
440 $styles .= '--plyr-audio-controls-background: ' . sanitize_hex_color($preset->background_color) . ';';
441 } else {
442 $styles .= '--plyr-audio-controls-background: ' . sanitize_hex_color($branding['color']) . ';';
443 }
444
445 if ($preset->control_color) {
446 $styles .= '--plyr-audio-control-color: ' . sanitize_hex_color($preset->control_color) . ';';
447 $styles .= '--plyr-range-thumb-background: ' . sanitize_hex_color($preset->control_color) . ';';
448 $styles .= '--plyr-range-fill-background: ' . sanitize_hex_color($preset->control_color) . ';';
449 $styles .= '--plyr-audio-progress-buffered-background: ' . Utility::hex2rgba(sanitize_hex_color($preset->control_color), 0.35) . ';';
450 $styles .= '--plyr-range-thumb-shadow: 0 1px 1px ' . Utility::hex2rgba(sanitize_hex_color($preset->control_color), 0.15) . ', 0 0 0 1px ' . Utility::hex2rgba(sanitize_hex_color($preset->control_color), 0.2) . ';';
451 } else {
452 $styles .= '--plyr-audio-control-color: #ffffff;';
453 $styles .= '--plyr-range-thumb-background: #ffffff;';
454 $styles .= '--plyr-range-fill-background: #ffffff;';
455 $styles .= '--plyr-audio-progress-buffered-background: ' . Utility::hex2rgba(sanitize_hex_color(sanitize_hex_color('#dcdcdc')), 0.35) . ';';
456 }
457 }
458
459 return $styles;
460 }
461
462 /**
463 * Get block attributes
464 *
465 * @param array $attributes
466 * @return array
467 */
468 public function getAttributes($attributes)
469 {
470 return $this->_sanitizeAttibutes($attributes);
471 }
472
473 /**
474 * Dynamic block output
475 *
476 * @param array $attributes
477 * @param string $content
478 * @return void
479 */
480 public function html($attributes, $content)
481 {
482 global $presto_player_instance;
483 if ($presto_player_instance === null) {
484 $presto_player_instance = 0;
485 }
486 $presto_player_instance++;
487
488 // html middleware
489 $load = $this->middleware($attributes, $content);
490
491 if (is_feed()) {
492 return $this->getFeedHtml($attributes);
493 }
494
495 if (LearnDash::isEnabled()) {
496 if (!LearnDash::shouldVideoLoad()) {
497 return false;
498 }
499 }
500
501 // let integrations filter loading capabilities
502 if (!apply_filters('presto_player_load_video', $load, $attributes, $content, $this->name)) {
503 // allow a custom fallback
504 if ($fallback = apply_filters('presto_player_load_video_fallback', false, $attributes, $content, $this)) {
505 return wp_kses_post($fallback);
506 }
507 return $this->getFallbackHTMLForUnauthorizeAccess();
508 }
509
510 // get template data
511 $data = apply_filters('presto_player_block_data', $this->getAttributes($attributes), $this);
512
513
514 // need and id and src
515 if (empty($data['id']) && empty($data['src'])) {
516 return false;
517 }
518
519 // TODO: child template system
520 ob_start();
521
522 if (!empty($data['id'])) {
523 echo "<!--presto-player:video_id=" . (int) $data['id'] . "-->";
524 }
525
526 if (file_exists(PRESTO_PLAYER_PLUGIN_DIR . "templates/{$this->template_name}.php")) {
527 include PRESTO_PLAYER_PLUGIN_DIR . "templates/{$this->template_name}.php";
528 }
529
530 $this->initComponentScript($data['id'], $data, $presto_player_instance);
531 $this->iframeFallback($data);
532
533 // output schema markup for optimized seo
534 $this->outputVideoSchemaMarkup($this->getSchema($data));
535
536 $template = ob_get_contents();
537 ob_end_clean();
538
539 return $template;
540
541 }
542
543 /**
544 * Get json data for video schema.
545 * https://developers.google.com/search/docs/appearance/structured-data/video#video-object
546 *
547 * @param array $data the block data
548 *
549 * @return array|bool
550 */
551 public function getSchema($data)
552 {
553
554 if (isset($data) && empty($data['id'])) {
555 return false;
556 }
557
558 if ('audio' === $data['type']) {
559 return false;
560 }
561
562 $visibility = $data['blockAttributes']['visibility'] ?? false;
563 if ($visibility && 'private' === $visibility) {
564 return false;
565 }
566
567 $title = $data['title'] ?? get_the_title();
568 if (empty($title)) {
569 return false;
570 }
571
572 $poster = $data['poster'] ?? '';
573 if (empty($poster)) {
574 return false;
575 }
576
577 $video = new Video((int) $data['id']);
578
579 return array(
580 // required:
581 '@context' => 'https://schema.org',
582 '@type' => 'VideoObject',
583 'name' => wp_kses_post($title),
584 'thumbnailUrl' => esc_url($poster),
585 'uploadDate' => wp_date('c', strtotime($video->getCreatedAt())),
586 // recommended:
587 'contentUrl' => esc_url($data['src'] ?? ''),
588 );
589
590 }
591
592 /**
593 * Output video schema markup.
594 *
595 * @param array $data the block data
596 *
597 * @return void|bool
598 */
599 public function outputVideoSchemaMarkup($data)
600 {
601
602 if (empty($data)) {
603 return false;
604 }
605
606 ?>
607 <script type="application/ld+json">
608 <?php
609 echo wp_json_encode($data);
610 ?>
611 </script>
612 <?php
613 }
614
615 /**
616 * Dynamically initialize component via script tag
617 * We have to do this because we cannot send arrays or object in plain html
618 */
619 public function initComponentScript($id = 0, $data = [], $instance = 1)
620 {
621 if (!$id) {
622 return;
623 }
624 ?>
625 <script>
626 var player = document.querySelector('presto-player#presto-player-<?php echo (int) $instance; ?>');
627 player.video_id = <?php echo (int) $id; ?>;
628 <?php
629 $attributes = apply_filters('presto_player/component/attributes', $this->component_attributes, $data);
630 foreach ($attributes as $attribute) { ?>
631 <?php if (isset($data[$attribute])) { ?>
632 player.<?php echo sanitize_text_field($attribute); ?> = <?php echo wp_json_encode($data[$attribute]); ?>;
633 <?php } ?>
634 <?php } ?>
635 </script>
636 <?php
637 }
638
639 /**
640 * Adds an iframe fallback script to the page in case js loading fails
641 *
642 * @return void
643 */
644 public function iframeFallback($data)
645 {
646 // must be vimeo or youtube
647 if (in_array($data['provider'], ['youtube', 'vimeo'])) {
648 add_filter('presto_player/scripts/load_iframe_fallback', '__return_true');
649 }
650 }
651
652 /**
653 * This function return HTML for unauthorized access or curtain.
654 *
655 * @return void
656 */
657 public function getFallbackHTMLForUnauthorizeAccess()
658 {
659 // Get the branding CSS variable.
660 $data = $this->getAttributes([]);
661 ob_start();
662 if (file_exists(PRESTO_PLAYER_PLUGIN_DIR . "templates/unauthorized.php")) {
663 include PRESTO_PLAYER_PLUGIN_DIR . "templates/unauthorized.php";
664 }
665 $template = ob_get_contents();
666 ob_end_clean();
667 return $template;
668 }
669
670 /**
671 * Return fallback html for feeds.
672 *
673 * @param array $atts array of attributes.
674 */
675 public function getFeedHtml($atts)
676 {
677 if (is_feed()) {
678 ob_start(); ?>
679
680 <?php if (in_array($this->name, array('self-hosted', 'bunny')) && !empty($atts['src'])) { ?>
681 <video controls preload="none">
682 <source src="<?php echo esc_url($atts['src']); ?>" />
683 </video>
684 <?php } ?>
685
686 <?php if ('audio' === $this->name && !empty($atts['src'])) { ?>
687 <audio controls preload="none">
688 <source src="<?php echo esc_url($atts['src']); ?>" />
689 </audio>
690 <?php } ?>
691
692 <?php if ('youtube' === $this->name && !empty($atts['video_id'])) { ?>
693 <?php echo wp_oembed_get('https://www.youtube.com/watch?v=' . $atts['video_id']); ?>
694 <?php } ?>
695
696 <?php if ('vimeo' === $this->name && !empty($atts['video_id'])) { ?>
697 <?php echo wp_oembed_get('https://vimeo.com/' . $atts['video_id']); ?>
698 <?php } ?>
699
700 <?php
701 return ob_get_clean();
702 }
703 }
704
705 /**
706 * Applies a default value to the attribute if attribute is not set.
707 *
708 * @param array $attributes array of attributes.
709 * @return array The merged attributes after applying defaults.
710 */
711 public function appyAttributeDefaults($attributes)
712 {
713 return wp_parse_args($attributes, $this->default_attributes);
714 }
715 }
716