PluginProbe
Jetpack – WP Security, Backup, Speed, & Growth / 16.3
Jetpack – WP Security, Backup, Speed, & Growth v16.3
16.3 16.3-beta 16.3-a.5 16.3-a.7 16.3-a.3 16.3-a.1 16.2 16.2-beta 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 All 508 releases
← All changes | jetpack_vendor/automattic/jetpack-videopress/src/class-initializer.php +268 -27 16.2 → 16.3 View file →
@@ -11,8 +11,16 @@
11 11 * Initialized the VideoPress package
12 12 */
13 13 class Initializer {
14 14
15 + /**
16 + * Bounds of the Latest Videos Playlist block's "Number of videos" setting;
17 + * the editor control uses the same range.
18 + */
19 + const LATEST_VIDEOS_PLAYLIST_MIN_COUNT = 1;
20 + const LATEST_VIDEOS_PLAYLIST_MAX_COUNT = 20;
21 + const LATEST_VIDEOS_PLAYLIST_DEFAULT_COUNT = 5;
22 +
15 23 const JETPACK_VIDEOPRESS_IFRAME_API_HANDLER = 'jetpack-videopress-iframe-api';
16 24
17 25 /**
18 26 * Initialization optinos
@@ -127,8 +135,9 @@
127 135 new WPCOM_REST_API_V2_Endpoint_VideoPress();
128 136 new WPCOM_REST_API_V2_Endpoint_VideoPress_Caption_Tracks();
129 137 new WPCOM_REST_API_V2_Attachment_VideoPress_Field();
130 138 new WPCOM_REST_API_V2_Attachment_VideoPress_Data();
139 + new WPCOM_REST_API_V2_Endpoint_VideoPress_Edits();
131 140 };
132 141 add_action( 'rest_api_init', $register_rest_api_v2_endpoints, 0 );
133 142 add_action( 'restapi_theme_init', $register_rest_api_v2_endpoints, 0 );
134 143
@@ -194,8 +203,11 @@
194 203 Caption_Tracks::init();
195 204 Initial_State::init();
196 205 XMLRPC::init();
197 206 Block_Editor_Content::init();
207 + Channel::init();
208 + Playlist_Index::init();
209 + Analytics_Dashboard::init();
198 210
199 211 /*
200 212 * These endpoints only add their routes on REST init, so defer calling
201 213 * init() (and autoloading the endpoint classes) until a REST request is
@@ -216,8 +228,13 @@
216 228 add_action( 'rest_api_init', array( $rest_endpoint, 'init' ), 0 );
217 229 }
218 230 self::register_oembed_providers();
219 231
232 + // In inline mode a VideoPress URL never needs the oEmbed round trip: skip
233 + // the fetch, and swap iframes already cached in post meta for a placeholder.
234 + add_filter( 'pre_oembed_result', array( __CLASS__, 'maybe_pre_oembed_inline_player' ), 10, 2 );
235 + add_filter( 'embed_oembed_html', array( __CLASS__, 'maybe_render_oembed_inline_player' ), 5, 4 );
236 +
220 237 // Enqueuethe VideoPress Iframe API script in the front-end.
221 238 add_filter( 'embed_oembed_html', array( __CLASS__, 'enqueue_videopress_iframe_api_script' ), 10, 4 );
222 239
223 240 if ( self::should_initialize_admin_ui() ) {
@@ -271,11 +288,30 @@
271 288 self::register_videopress_video_block();
272 289
273 290 // Register Video Playlist block.
274 291 self::register_videopress_playlist_block();
292 +
293 + // Register Latest Videos Playlist block.
294 + self::register_videopress_latest_videos_playlist_block();
295 +
296 + // Register All Playlists block.
297 + self::register_videopress_all_playlists_block();
275 298 }
276 299
277 300 /**
301 + * Register the All Playlists block, which lists the site's Video Playlist
302 + * blocks from the playlist index.
303 + *
304 + * @param string|null $metadata_file Path to the block.json metadata file. Defaults to the
305 + * package build output; tests can point it at a fixture.
306 + *
307 + * @return void
308 + */
309 + public static function register_videopress_all_playlists_block( $metadata_file = null ) {
310 + All_Playlists_Block::register( $metadata_file );
311 + }
312 +
313 + /**
278 314 * VideoPress video block render method
279 315 *
280 316 * @global \WP_Embed $wp_embed WordPress embed handler.
281 317 *
@@ -415,9 +451,24 @@
415 451
416 452 $video_wrapper = '';
417 453 $video_wrapper_classes = 'jetpack-videopress-player__wrapper';
418 454
419 - if ( $videopress_url ) {
455 + // Preview on hover drives the player through the iframe API, so it keeps the iframe.
456 + if ( $guid && ! $is_poh_enabled && Inline_Player::is_enabled() ) {
457 + $video_wrapper = sprintf(
458 + '<div class="%s">%s</div>',
459 + $video_wrapper_classes,
460 + Inline_Player::render(
461 + $guid,
462 + Inline_Player::get_player_options( $block_attributes ),
463 + $block_attributes['videoRatio'] ?? null,
464 + array(
465 + 'poster' => Inline_Player::get_poster_url( $guid, $block_attributes ),
466 + 'title' => $block_attributes['title'] ?? '',
467 + )
468 + )
469 + );
470 + } elseif ( $videopress_url ) {
420 471 $videopress_url = wp_kses_post( $videopress_url );
421 472
422 473 /*
423 474 * Provide a fallback iframe for when the oEmbed endpoint fails, e.g.
@@ -614,8 +665,77 @@
614 665 );
615 666 }
616 667
617 668 /**
669 + * Register the Latest Videos Playlist block.
670 + *
671 + * It reuses the Video Playlist block's registered view script and styles, so
672 + * it is only registered once that block is. Its inner Video Playlist block is
673 + * the editor canvas only: the front end renders the newest videos fresh.
674 + *
675 + * @param string|null $metadata_file Path to the block.json metadata file. Defaults to the
676 + * package build output; tests can point it at a fixture.
677 + *
678 + * @return void
679 + */
680 + public static function register_videopress_latest_videos_playlist_block( $metadata_file = null ) {
681 + if ( ! \WP_Block_Type_Registry::get_instance()->is_registered( 'videopress/playlist' ) ) {
682 + return;
683 + }
684 +
685 + if ( null === $metadata_file ) {
686 + $metadata_file = __DIR__ . '/../build/block-editor/blocks/latest-videos-playlist/block.json';
687 + }
688 +
689 + if ( ! file_exists( $metadata_file ) ) {
690 + return;
691 + }
692 +
693 + $metadata = json_decode(
694 + // phpcs:ignore WordPress.WP.AlternativeFunctions.file_get_contents_file_get_contents
695 + file_get_contents( $metadata_file )
696 + );
697 +
698 + if ( empty( $metadata->name )
699 + || \WP_Block_Type_Registry::get_instance()->is_registered( $metadata->name )
700 + ) {
701 + return;
702 + }
703 +
704 + register_block_type(
705 + $metadata_file,
706 + array(
707 + 'render_callback' => array( __CLASS__, 'render_videopress_latest_videos_playlist_block' ),
708 + 'skip_inner_blocks' => true,
709 + )
710 + );
711 + }
712 +
713 + /**
714 + * Latest Videos Playlist block render callback: the newest VideoPress videos
715 + * on the site, rendered by the Video Playlist block's callback.
716 + *
717 + * @param array $block_attributes Block attributes.
718 + * @param string $content Current block markup, unused: the inner block is never rendered.
719 + * @param \WP_Block|null $block Current block.
720 + *
721 + * @return string Block markup, or an empty string when the site has no VideoPress videos.
722 + */
723 + public static function render_videopress_latest_videos_playlist_block( $block_attributes, $content = '', $block = null ) {
724 + $count = isset( $block_attributes['count'] ) && is_numeric( $block_attributes['count'] )
725 + ? (int) $block_attributes['count']
726 + : self::LATEST_VIDEOS_PLAYLIST_DEFAULT_COUNT;
727 + $count = max( self::LATEST_VIDEOS_PLAYLIST_MIN_COUNT, min( self::LATEST_VIDEOS_PLAYLIST_MAX_COUNT, $count ) );
728 +
729 + $block_attributes['videos'] = Data::get_latest_videopress_playlist_entries( $count );
730 +
731 + // Dynamic playlists have no title of their own, so no heading either.
732 + unset( $block_attributes['playlistTitle'], $block_attributes['showPlaylistTitle'] );
733 +
734 + return self::render_videopress_playlist_block( $block_attributes, '', $block );
735 + }
736 +
737 + /**
618 738 * Sanitize the playlist block's videos attribute into rendering-ready entries.
619 739 *
620 740 * @param mixed $videos Raw attribute value.
621 741 *
@@ -620,9 +740,9 @@
620 740 * @param mixed $videos Raw attribute value.
621 741 *
622 742 * @return array Entries with guid, title, durationMs, height and poster keys.
623 743 */
624 - private static function sanitize_playlist_entries( $videos ) {
744 + public static function sanitize_playlist_entries( $videos ) {
625 745 if ( ! is_array( $videos ) ) {
626 746 return array();
627 747 }
628 748
@@ -749,9 +869,9 @@
749 869 /**
750 870 * Video Playlist block render callback.
751 871 *
752 872 * @param array $block_attributes Block attributes.
753 - * @param string $content Current block markup.
873 + * @param string $content Rendered inner blocks: the title heading, when there is one.
754 874 * @param \WP_Block|null $block Current block.
755 875 *
756 876 * @return string Block markup, or an empty string when the playlist has no playable entries.
757 877 */
@@ -777,8 +897,14 @@
777 897 $layout = isset( $block_attributes['layout'] ) && in_array( $block_attributes['layout'], array( 'side-rail', 'grid', 'strip' ), true )
778 898 ? $block_attributes['layout']
779 899 : 'side-rail';
780 900
901 + $show_player = $enabled( 'showPlayer' );
902 + // A hidden player can still be revealed by the first click on an entry.
903 + $reveal_player = ! $show_player
904 + && isset( $block_attributes['entryClickAction'] )
905 + && 'show-player' === $block_attributes['entryClickAction'];
906 + $has_player = $show_player || $reveal_player;
781 907 $show_thumbnail = $enabled( 'showThumbnail' );
782 908 $show_title = $enabled( 'showTitle' );
783 909 $show_res = $enabled( 'showResolution' );
784 910 $show_duration = $enabled( 'showDuration' );
@@ -789,8 +915,11 @@
789 915 $classes = array( 'videopress-playlist', 'is-layout-' . $layout );
790 916 if ( $enabled( 'darkPlayer', false ) ) {
791 917 $classes[] = 'is-dark';
792 918 }
919 + if ( ! $show_player ) {
920 + $classes[] = 'hide-player';
921 + }
793 922 if ( ! $show_thumbnail ) {
794 923 $classes[] = 'hide-thumbnails';
795 924 }
796 925 if ( ! $show_title ) {
@@ -866,28 +995,57 @@
866 995 $duration_markup = '' !== $timecode
867 996 ? sprintf( '<span class="videopress-playlist__entry-duration">%s</span>', esc_html( $timecode ) )
868 997 : '';
869 998
999 + if ( $has_player ) {
1000 + $is_current = $show_player && 0 === $index;
1001 + $entry_open = sprintf(
1002 + '<button type="button" class="videopress-playlist__select%1$s"%2$s data-guid="%3$s" data-embed-url="%4$s" data-title="%5$s" data-position="%6$s" data-details="%7$s" data-progress="%8$s">',
1003 + $is_current ? ' is-current' : '',
1004 + $is_current ? ' aria-current="true"' : '',
1005 + esc_attr( $entry['guid'] ),
1006 + esc_url( self::playlist_embed_url( $entry['guid'], true, $muted ) ),
1007 + esc_attr( $title ),
1008 + esc_attr( $position ),
1009 + esc_attr( $details ),
1010 + esc_attr( $progress )
1011 + );
1012 + $entry_close = '</button>';
1013 + } else {
1014 + // No player to load the video into: the entry opens its VideoPress page instead.
1015 + $entry_open = sprintf(
1016 + '<a class="videopress-playlist__select" href="%1$s" target="_blank" rel="noopener noreferrer" data-guid="%2$s" data-title="%3$s" data-position="%4$s" data-details="%5$s">',
1017 + /**
1018 + * Filters where a playlist entry opens when the block has no player.
1019 + *
1020 + * @since 0.55.0
1021 + *
1022 + * @param string $url The video's page on videopress.com.
1023 + * @param string $guid The video GUID.
1024 + */
1025 + esc_url( apply_filters( 'videopress_playlist_entry_url', 'https://videopress.com/v/' . $entry['guid'], $entry['guid'] ) ),
1026 + esc_attr( $entry['guid'] ),
1027 + esc_attr( $title ),
1028 + esc_attr( $position ),
1029 + esc_attr( $details )
1030 + );
1031 + $entry_close = '</a>';
1032 + }
1033 +
870 1034 $items .= sprintf(
871 - '<li class="videopress-playlist__entry"><button type="button" class="videopress-playlist__select%1$s"%2$s data-guid="%3$s" data-embed-url="%4$s" data-title="%5$s" data-position="%6$s" data-details="%7$s" data-progress="%8$s">' .
872 - '%9$s<span class="videopress-playlist__entry-thumb"><span class="videopress-playlist__entry-flag">%10$s</span>%15$s%11$s</span>' .
873 - '<span class="videopress-playlist__entry-body"><span class="videopress-playlist__entry-title">%12$s</span><span class="videopress-playlist__entry-meta">%13$s%14$s</span></span>' .
874 - '</button></li>',
875 - 0 === $index ? ' is-current' : '',
876 - 0 === $index ? ' aria-current="true"' : '',
877 - esc_attr( $entry['guid'] ),
878 - esc_url( self::playlist_embed_url( $entry['guid'], true, $muted ) ),
879 - esc_attr( $title ),
880 - esc_attr( $position ),
881 - esc_attr( $details ),
882 - esc_attr( $progress ),
1035 + '<li class="videopress-playlist__entry">%1$s' .
1036 + '%2$s<span class="videopress-playlist__entry-thumb"><span class="videopress-playlist__entry-flag">%3$s</span>%4$s%5$s</span>' .
1037 + '<span class="videopress-playlist__entry-body"><span class="videopress-playlist__entry-title">%6$s</span><span class="videopress-playlist__entry-meta">%7$s%8$s</span></span>' .
1038 + '%9$s</li>',
1039 + $entry_open,
883 1040 $number_markup,
884 1041 esc_html__( 'Playing', 'jetpack-videopress-pkg' ),
1042 + $lock_markup,
885 1043 $time_markup,
886 1044 esc_html( $title ),
887 1045 $resolution_markup,
888 1046 $duration_markup,
889 - $lock_markup
1047 + $entry_close
890 1048 );
891 1049 }
892 1050
893 1051 $first = $entries[0];
@@ -914,17 +1072,30 @@
914 1072 esc_html( $count_label . ' · ' . $runtime_label )
915 1073 )
916 1074 : '';
917 1075
918 - $stage_markup = sprintf(
919 - '<div class="videopress-playlist__stage">' .
920 - '<div class="videopress-playlist__player"><iframe class="videopress-playlist__iframe" title="%1$s" src="%2$s" allowfullscreen allow="clipboard-write"></iframe></div>%3$s</div>',
921 - esc_attr( $first_title ),
922 - esc_url( self::playlist_embed_url( $first['guid'], false, $muted ) ),
923 - $now_markup
924 - );
1076 + if ( $show_player ) {
1077 + $stage_markup = sprintf(
1078 + '<div class="videopress-playlist__stage">' .
1079 + '<div class="videopress-playlist__player"><iframe class="videopress-playlist__iframe" title="%1$s" src="%2$s" allowfullscreen allow="clipboard-write"></iframe></div>%3$s</div>',
1080 + esc_attr( $first_title ),
1081 + esc_url( self::playlist_embed_url( $first['guid'], false, $muted ) ),
1082 + $now_markup
1083 + );
1084 + } elseif ( $reveal_player ) {
1085 + // No src: nothing loads until the view script reveals the stage on a click.
1086 + $stage_markup = sprintf(
1087 + '<div class="videopress-playlist__stage" hidden>' .
1088 + '<div class="videopress-playlist__player"><iframe class="videopress-playlist__iframe" title="%1$s" allowfullscreen allow="clipboard-write"></iframe></div></div>%2$s',
1089 + esc_attr( $first_title ),
1090 + $now_markup
1091 + );
1092 + } else {
1093 + // Without a player only the grid layout's runtime line remains of the stage.
1094 + $stage_markup = $now_markup;
1095 + }
925 1096
926 - $progress_markup = '' !== $total_timecode
1097 + $progress_markup = $has_player && '' !== $total_timecode
927 1098 ? sprintf(
928 1099 '<span class="videopress-playlist__list-progress">%s</span>',
929 1100 /* translators: 1: position of the current video. 2: number of videos. 3: total playlist timecode. */
930 1101 esc_html( sprintf( __( '%1$d / %2$d · %3$s total', 'jetpack-videopress-pkg' ), 1, $count, $total_timecode ) )
@@ -936,9 +1107,11 @@
936 1107 '<div class="videopress-playlist__list-header">' .
937 1108 '<span class="videopress-playlist__list-label videopress-playlist__list-label--rail">%1$s</span>' .
938 1109 '<span class="videopress-playlist__list-label videopress-playlist__list-label--strip">%2$s</span>%3$s%4$s</div>' .
939 1110 '<ol class="videopress-playlist__entries">%5$s</ol></div>',
940 - esc_html__( 'Up next', 'jetpack-videopress-pkg' ),
1111 + $show_player
1112 + ? esc_html__( 'Up next', 'jetpack-videopress-pkg' )
1113 + : esc_html__( 'Playlist', 'jetpack-videopress-pkg' ),
941 1114 /* translators: %s: number of videos in the playlist, e.g. "5 videos". */
942 1115 esc_html( sprintf( __( 'Playlist — %s', 'jetpack-videopress-pkg' ), $count_label ) ),
943 1116 $list_meta_markup,
944 1117 $progress_markup,
@@ -975,11 +1148,21 @@
975 1148 }
976 1149
977 1150 $wrapper_attributes = get_block_wrapper_attributes( $wrapper_extra_attributes );
978 1151
1152 + // The Heading inner block saved with the post; an empty title renders none.
1153 + $playlist_title = isset( $block_attributes['playlistTitle'] ) && is_string( $block_attributes['playlistTitle'] )
1154 + ? trim( $block_attributes['playlistTitle'] )
1155 + : '';
1156 + $content = is_string( $content ) ? trim( $content ) : '';
1157 + $heading_markup = $enabled( 'showPlaylistTitle' ) && '' !== $playlist_title && '' !== $content
1158 + ? '<div class="videopress-playlist__heading">' . $content . '</div>'
1159 + : '';
1160 +
979 1161 return sprintf(
980 - '<figure %1$s><div class="videopress-playlist__body">%2$s%3$s</div></figure>',
1162 + '<figure %1$s>%2$s<div class="videopress-playlist__body">%3$s%4$s</div></figure>',
981 1163 $wrapper_attributes,
1164 + $heading_markup,
982 1165 $stage_markup,
983 1166 $list_markup
984 1167 );
985 1168 }
@@ -995,9 +1178,9 @@
995 1178 *
996 1179 * @return string|false
997 1180 */
998 1181 public static function enqueue_videopress_iframe_api_script( $cache, $url, $attr, $post_ID ) { // phpcs:ignore VariableAnalysis.CodeAnalysis.VariableAnalysis.UnusedVariable
999 - if ( Utils::is_videopress_url( $url ) ) {
1182 + if ( Utils::is_videopress_url( $url ) && ! Inline_Player::is_enabled() ) {
1000 1183 // Enqueue the VideoPress IFrame API in the front-end.
1001 1184 wp_enqueue_script(
1002 1185 self::JETPACK_VIDEOPRESS_IFRAME_API_HANDLER,
1003 1186 'https://s0.wp.com/wp-content/plugins/video/assets/js/videojs/videopress-iframe-api.js',
@@ -1007,6 +1190,64 @@
1007 1190 );
1008 1191 }
1009 1192
1010 1193 return $cache;
1194 + }
1195 +
1196 + /**
1197 + * Short-circuit the oEmbed request for VideoPress URLs when the inline player is on.
1198 + *
1199 + * @param null|string $result The oEmbed result, null to let WordPress fetch it.
1200 + * @param string $url The URL being embedded.
1201 + * @return null|string Inline player markup, or the untouched result.
1202 + */
1203 + public static function maybe_pre_oembed_inline_player( $result, $url ) {
1204 + $inline = self::render_inline_player_for_url( $url );
1205 +
1206 + return null === $inline ? $result : $inline;
1207 + }
1208 +
1209 + /**
1210 + * Replace a VideoPress oEmbed iframe (fresh or cached) with an inline player when the inline player is on.
1211 + *
1212 + * @param string|false $cache The oEmbed HTML.
1213 + * @param string $url The URL being embedded.
1214 + * @param array $attr Shortcode attributes.
1215 + * @param int $post_ID Post ID.
1216 + * @return string|false Inline player markup, or the untouched HTML.
1217 + */
1218 + public static function maybe_render_oembed_inline_player( $cache, $url, $attr, $post_ID = null ) { // phpcs:ignore VariableAnalysis.CodeAnalysis.VariableAnalysis.UnusedVariable
1219 + if ( ! is_string( $cache ) || false === strpos( $cache, '<iframe' ) ) {
1220 + return $cache;
1221 + }
1222 +
1223 + $inline = self::render_inline_player_for_url( $url );
1224 +
1225 + return null === $inline ? $cache : $inline;
1226 + }
1227 +
1228 + /**
1229 + * Build inline player markup for a videopress.com URL, honoring the player parameters in its query string.
1230 + *
1231 + * @param string $url The URL being embedded.
1232 + * @return string|null Markup, or null when the URL is not a VideoPress video or the inline player is off.
1233 + */
1234 + private static function render_inline_player_for_url( $url ) {
1235 + if ( ! Inline_Player::is_enabled() ) {
1236 + return null;
1237 + }
1238 +
1239 + $guid = Utils::extract_videopress_guid_from_url( $url );
1240 + if ( null === $guid ) {
1241 + return null;
1242 + }
1243 +
1244 + $attributes = Inline_Player::get_attributes_from_embed_url( $url );
1245 +
1246 + return Inline_Player::render(
1247 + $guid,
1248 + Inline_Player::get_player_options( $attributes ),
1249 + null,
1250 + array( 'poster' => Inline_Player::get_poster_url( $guid, $attributes ) )
1251 + );
1011 1252 }
1012 1253 }