PluginProbe ʕ •ᴥ•ʔ
Jetpack – WP Security, Backup, Speed, & Growth / 14.2.2
Jetpack – WP Security, Backup, Speed, & Growth v14.2.2
12.0.3 12.1.3 12.2.3 12.3.2 12.4.2 12.5.2 12.6.4 12.7.3 12.8.3 12.9.5 13.0.2 13.1.5 13.2.4 13.3.3 13.4.5 13.5.2 13.6.2 13.7.2 13.8.3 13.9.2 14.0.1 14.1.1 14.2.2 14.3.1 14.4.2 14.5.1 14.6.1 14.7.1 14.8.1 14.9.2 15.0.3 15.1.2 15.2.1 15.3.2 15.4.1 15.5.1 15.6.1 15.7.2 15.8.1 15.9.2 16.0.2 16.1.3 16.2-a.5 16.2-a.3 16.1.2 16.2-a.1 16.1.1 16.1 16.1-beta 16.1-beta.2 16.1-beta.3 16.1-a.5 16.1-a.3 16.0.1 16.1-a.1 16.0 16.0-beta 16.0-a.7 16.0-a.5 15.9.1 16.0-a.3 16.0-a.1 15.9 15.9-beta 15.9-a.7 15.9-a.5 15.9-a.3 15.9-a.1 15.8 15.8-beta 15.8-a.7 15.8-a.5 5.2.5 5.3.4 5.4.4 5.5.5 5.6.5 5.7.5 5.8.4 5.9.4 6.0.4 6.1 6.1.1 6.1.2 6.1.3 6.1.4 6.1.5 6.2 6.2.1 6.2.2 6.2.3 6.2.4 6.2.5 6.3 6.3.1 6.3.2 6.3.3 6.3.4 6.3.5 6.3.6 6.3.7 6.4 6.4.1 6.4.2 6.4.3 6.4.4 6.4.5 6.4.6 6.5 6.5.1 6.5.2 6.5.3 6.5.4 6.6 6.6.1 6.6.2 6.6.3 6.6.4 6.6.5 6.7 6.7.1 6.7.2 6.7.3 6.7.4 6.8 6.8.1 6.8.2 6.8.3 6.8.4 6.8.5 6.9 6.9.1 6.9.2 6.9.3 6.9.4 7.0 7.0.1 7.0.2 7.0.3 7.0.4 7.0.5 7.1 7.1.1 7.1.2 7.1.3 7.1.4 7.1.5 7.2 7.2.1 7.2.1.1 7.2.2 7.2.3 7.2.4 7.2.5 7.3 7.3.0.1 7.3.1 7.3.1.1 7.3.2 7.3.3 7.3.4 7.3.5 7.4 7.4.1 7.4.2 7.4.3 7.4.4 7.4.5 7.5 7.5.0.1 7.5.1 7.5.2 7.5.3 7.5.4 7.5.5 7.5.6 7.5.7 7.6 7.6.1 7.6.2 7.6.3 7.6.4 7.7 7.7.1 7.7.2 7.7.3 7.7.4 7.7.5 7.7.6 7.8 7.8.1 7.8.2 7.8.3 7.8.4 7.9 7.9.1 7.9.2 7.9.3 7.9.4 8.0 8.0.1 8.0.2 8.0.3 8.1 8.1.1 8.1.2 8.1.3 8.1.4 8.2 8.2.0.1 8.2.1 8.2.2 8.2.3 8.2.4 8.2.5 8.2.6 8.3 8.3.1 8.3.2 8.3.3 8.4 8.4.1 8.4.2 8.4.3 8.4.4 8.4.5 8.5 8.5.1 8.5.2 8.5.3 8.6 8.6.1 8.6.2 8.6.3 8.6.4 8.7 8.7.0.1 8.7.1 8.7.2 8.7.3 8.7.4 8.8 8.8.1 8.8.2 8.8.3 8.8.4 8.8.5 8.9 8.9.1 8.9.2 8.9.3 8.9.4 9.0 9.0.1 9.0.2 9.0.3 9.0.4 9.0.5 9.1 9.1.1 9.1.2 9.1.3 9.2 9.2.1 9.2.2 9.2.3 9.2.4 9.3 9.3.1 9.3.2 9.3.3 9.3.4 9.3.5 9.4 9.4.1 9.4.2 9.4.3 9.4.4 9.5 9.5.1 9.5.2 9.5.3 9.5.4 9.5.5 9.6 9.6.1 9.6.2 9.6.3 9.6.4 9.7 9.7.1 9.7.2 15.7-beta.2 9.7.3 15.7.1 9.8 15.8-a.1 9.8.1 15.8-a.3 9.8.2 2.0.9 9.8.3 2.1.7 9.9 2.2.10 9.9.1 2.3.10 9.9.2 2.4.7 9.9.3 2.5.5 2.6.6 2.7.5 2.8.5 2.9.6 3.0.6 3.1.5 3.2.5 3.3.6 3.4.6 3.5.6 3.6.4 3.7.5 3.8.5 3.9.10 4.0.7 4.1.4 4.2.5 4.3.5 4.4.5 4.5.3 4.6.3 4.7.4 4.8.5 4.9.3 5.0.3 5.1.4 trunk 10.0 10.0.1 10.0.2 10.1 10.1.1 10.1.2 10.2 10.2.1 10.2.2 10.2.3 10.3 10.3.1 10.3.2 10.4 10.4.1 10.4.2 10.5 10.5.1 10.5.2 10.5.3 10.6 10.6.1 10.6.2 10.7 10.7.1 10.7.2 10.8 10.8.1 10.8.2 10.9 10.9.1 10.9.2 10.9.3 11.0 11.0.1 11.0.2 11.1 11.1.1 11.1.2 11.1.3 11.1.4 11.2 11.2.1 11.2.2 11.3 11.3.1 11.3.2 11.3.3 11.3.4 11.4 11.4.1 11.4.2 11.5 11.5.1 11.5.2 11.5.3 11.6 11.6.1 11.6.2 11.7 11.7.1 11.7.2 11.7.3 11.8 11.8.3 11.8.4 11.8.5 11.8.6 11.9 11.9.1 11.9.2 11.9.3 12.0 12.0.1 12.0.2 12.1 12.1.1 12.1.2 12.2 12.2.1 12.2.2 12.3 12.3.1 12.4 12.4.1 12.5 12.5.1 12.6 12.6.1 12.6.2 12.6.3 12.7 12.7.1 12.7.2 12.8 12.8.1 12.8.2 12.9 12.9.1 12.9.2 12.9.3 12.9.4 13.0 13.0.1 13.1 13.1.1 13.1.2 13.1.3 13.1.4 13.2 13.2.1 13.2.2 13.2.3 13.3 13.3.1 13.3.2 13.4 13.4.1 13.4.2 13.4.3 13.4.4 13.5 13.5.1 13.6 13.6.1 13.7 13.7.1 13.8 13.8.1 13.8.2 13.9 13.9.1 14.0 14.1 14.2 14.2.1 14.3 14.4 14.4.1 14.5 14.6 14.7 14.8 14.9 14.9.1 15.0 15.0.1 15.0.2 15.1 15.1.1 15.2 15.3 15.3.1 15.4 15.5 15.6 15.7 15.7-a.1 15.7-a.3 15.7-a.5 15.7-a.7 15.7-beta
jetpack / extensions / blocks / story / story.php
jetpack / extensions / blocks / story Last commit date
story.php 1 year ago
story.php
502 lines
1 <?php
2 /**
3 * Story Block.
4 *
5 * @since 8.6.1
6 *
7 * @package automattic/jetpack
8 */
9
10 namespace Automattic\Jetpack\Extensions\Story;
11
12 use Automattic\Jetpack\Blocks;
13 use Automattic\Jetpack\Connection\Connection_Assets;
14 use Jetpack;
15 use Jetpack_Gutenberg;
16 use Jetpack_PostImages;
17
18 const EMBED_SIZE = array( 360, 640 ); // twice as many pixels for retina displays.
19 const CROP_UP_TO = 0.2;
20 const MAX_BULLETS = 7;
21 const IMAGE_BREAKPOINTS = '(max-width: 460px) 576w, (max-width: 614px) 768w, 120vw'; // 120vw to match the 20% CROP_UP_TO ratio
22
23 /**
24 * Registers the block for use in Gutenberg
25 * This is done via an action so that we can disable
26 * registration if we need to.
27 */
28 function register_block() {
29 Blocks::jetpack_register_block(
30 __DIR__,
31 array( 'render_callback' => __NAMESPACE__ . '\render_block' )
32 );
33 }
34 add_action( 'init', __NAMESPACE__ . '\register_block' );
35
36 /**
37 * Compare 2 urls and return true if they likely correspond to the same resource.
38 * Ignore scheme, ports, query params and hashes and only compare hostname and pathname.
39 *
40 * @param string $url1 - First url used in comparison.
41 * @param string $url2 - Second url used in comparison.
42 *
43 * @return boolean
44 */
45 function is_same_resource( $url1, $url2 ) {
46 $url1_parsed = wp_parse_url( $url1 );
47 $url2_parsed = wp_parse_url( $url2 );
48 return isset( $url1_parsed['host'] ) &&
49 isset( $url2_parsed['host'] ) &&
50 isset( $url1_parsed['path'] ) &&
51 isset( $url2_parsed['path'] ) &&
52 $url1_parsed['host'] === $url2_parsed['host'] &&
53 $url1_parsed['path'] === $url2_parsed['path'];
54 }
55
56 /**
57 * Enrich media files retrieved from the story block attributes
58 * with extra information we can retrieve from the media library.
59 *
60 * @param array $media_files - List of media, each as an array containing the media attributes.
61 *
62 * @return array $media_files
63 */
64 function enrich_media_files( $media_files ) {
65 return array_filter(
66 array_map(
67 function ( $media_file ) {
68 if ( 'image' === $media_file['type'] ) {
69 return enrich_image_meta( $media_file );
70 }
71 // VideoPress videos can sometimes have type 'file', and mime 'video/videopress' or 'video/mp4'.
72 // Let's fix `type` for those.
73 if ( 'file' === $media_file['type'] && str_starts_with( $media_file['mime'], 'video' ) ) {
74 $media_file['type'] = 'video';
75 }
76 if ( 'video' !== $media_file['type'] ) { // we only support images and videos at this point.
77 return null;
78 }
79 return enrich_video_meta( $media_file );
80 },
81 $media_files
82 )
83 );
84 }
85
86 /**
87 * Enrich image information with extra data we can retrieve from the media library.
88 * Add missing `width`, `height`, `srcset`, `sizes`, `title`, `alt` and `caption` properties to the image.
89 *
90 * @param array $media_file - An array containing the media attributes for a specific image.
91 *
92 * @return array $media_file_enriched
93 */
94 function enrich_image_meta( $media_file ) {
95 $attachment_id = isset( $media_file['id'] ) ? $media_file['id'] : null;
96 $image = wp_get_attachment_image_src( $attachment_id, 'full', false );
97 if ( ! $image ) {
98 return $media_file;
99 }
100 list( $src, $width, $height ) = $image;
101 // Bail if url stored in block attributes is different than the media library one for that id.
102 if ( isset( $media_file['url'] ) && ! is_same_resource( $media_file['url'], $src ) ) {
103 return $media_file;
104 }
105 $image_meta = wp_get_attachment_metadata( $attachment_id );
106 if ( ! is_array( $image_meta ) ) {
107 return $media_file;
108 }
109 $size_array = array( absint( $width ), absint( $height ) );
110 return array_merge(
111 $media_file,
112 array(
113 'width' => absint( $width ),
114 'height' => absint( $height ),
115 'srcset' => wp_calculate_image_srcset( $size_array, $src, $image_meta, $attachment_id ),
116 'sizes' => IMAGE_BREAKPOINTS,
117 'title' => get_the_title( $attachment_id ),
118 'alt' => get_post_meta( $attachment_id, '_wp_attachment_image_alt', true ),
119 'caption' => wp_get_attachment_caption( $attachment_id ),
120 )
121 );
122 }
123
124 /**
125 * Enrich video information with extra data we can retrieve from the media library.
126 * Add missing `width`, `height`, `alt`, `url`, `title`, `caption` and `poster` properties to the image.
127 *
128 * @param array $media_file - An array containing the media attributes for a specific video.
129 *
130 * @return array $media_file_enriched
131 */
132 function enrich_video_meta( $media_file ) {
133 $attachment_id = isset( $media_file['id'] ) ? $media_file['id'] : null;
134 $video_meta = wp_get_attachment_metadata( $attachment_id );
135 if ( ! $video_meta ) {
136 return $media_file;
137 }
138
139 $video_url = ! empty( $video_meta['original']['url'] ) ? $video_meta['original']['url'] : wp_get_attachment_url( $attachment_id );
140
141 // Set the poster attribute for the video tag if a poster image is available.
142 $poster_url = null;
143 if ( ! empty( $video_meta['videopress']['poster'] ) ) {
144 $poster_url = $video_meta['videopress']['poster'];
145 } elseif ( ! empty( $video_meta['thumb'] ) ) {
146 $poster_url = str_replace( wp_basename( $video_url ), $video_meta['thumb'], $video_url );
147 }
148
149 if ( $poster_url ) {
150 // Use the global content width for thumbnail resize so we match the `w=` query parameter
151 // that jetpack is going to add when "Enable site accelerator" is enabled for images.
152 $content_width = (int) Jetpack::get_content_width();
153 $new_width = $content_width > 0 ? $content_width : EMBED_SIZE[0];
154 $poster_url = add_query_arg( 'w', $new_width, $poster_url );
155 }
156
157 return array_merge(
158 $media_file,
159 array(
160 'width' => absint( ! empty( $video_meta['width'] ) ? $video_meta['width'] : $media_file['width'] ),
161 'height' => absint( ! empty( $video_meta['height'] ) ? $video_meta['height'] : $media_file['height'] ),
162 'alt' => ! empty( $video_meta['videopress']['description'] ) ? $video_meta['videopress']['description'] : $media_file['alt'],
163 'url' => $video_url,
164 'title' => get_the_title( $attachment_id ),
165 'caption' => wp_get_attachment_caption( $attachment_id ),
166 'poster' => $poster_url,
167 )
168 );
169 }
170
171 /**
172 * Render an image inside a slide
173 *
174 * @param array $media - Image information.
175 *
176 * @return string
177 */
178 function render_image( $media ) {
179 if ( empty( $media['id'] ) || empty( $media['url'] ) ) {
180 return __( 'Error retrieving media', 'jetpack' );
181 }
182 $image = wp_get_attachment_image_src( $media['id'], 'full', false );
183 if ( $image ) {
184 list( $src, $width, $height ) = $image;
185 }
186
187 // if image does not match.
188 if ( ! $image || isset( $media['url'] ) && ! is_same_resource( $media['url'], $src ) ) {
189 $width = isset( $media['width'] ) ? $media['width'] : null;
190 $height = isset( $media['height'] ) ? $media['height'] : null;
191 $title = isset( $media['title'] ) ? $media['title'] : '';
192 $alt = isset( $media['alt'] ) ? $media['alt'] : '';
193 return sprintf(
194 '<img
195 title="%1$s"
196 alt="%2$s"
197 class="wp-block-jetpack-story_image wp-story-image %3$s"
198 src="%4$s"
199 />',
200 esc_attr( $title ),
201 esc_attr( $alt ),
202 $width && $height ? get_image_crop_class( $width, $height ) : '',
203 esc_attr( $media['url'] )
204 );
205 }
206
207 $crop_class = get_image_crop_class( $width, $height );
208 // need to specify the size of the embed so it picks an image that is large enough for the `src` attribute
209 // `sizes` is optimized for 1080x1920 (9:16) images
210 // Note that the Story block does not have thumbnail support, it will load the right
211 // image based on the viewport size only.
212 return wp_get_attachment_image(
213 $media['id'],
214 EMBED_SIZE,
215 false,
216 array(
217 'class' => sprintf( 'wp-story-image wp-image-%d %s', $media['id'], $crop_class ),
218 'sizes' => IMAGE_BREAKPOINTS,
219 'title' => get_the_title( $media['id'] ),
220 )
221 );
222 }
223
224 /**
225 * Return the css crop class if image width and height requires it
226 *
227 * @param int $width - Image width.
228 * @param int $height - Image height.
229 *
230 * @return string The CSS class which will display a cropped image
231 */
232 function get_image_crop_class( $width, $height ) {
233 $crop_class = '';
234 $width = (int) $width;
235 $height = (int) $height;
236 if ( ! $width || ! $height ) {
237 return $crop_class;
238 }
239 $media_aspect_ratio = $width / $height;
240 $target_aspect_ratio = EMBED_SIZE[0] / EMBED_SIZE[1];
241 if ( $media_aspect_ratio >= $target_aspect_ratio ) {
242 // image wider than canvas.
243 $media_too_wide_to_crop = $media_aspect_ratio > $target_aspect_ratio / ( 1 - CROP_UP_TO );
244 if ( ! $media_too_wide_to_crop ) {
245 $crop_class = 'wp-story-crop-wide';
246 }
247 } else {
248 // image narrower than canvas.
249 $media_too_narrow_to_crop = $media_aspect_ratio < $target_aspect_ratio * ( 1 - CROP_UP_TO );
250 if ( ! $media_too_narrow_to_crop ) {
251 $crop_class = 'wp-story-crop-narrow';
252 }
253 }
254 return $crop_class;
255 }
256
257 /**
258 * Returns a URL for the site icon.
259 *
260 * @param int $size - Size for (square) sitei icon.
261 * @param string $fallback - Fallback URL to use if no site icon is found.
262 *
263 * @return string
264 */
265 function get_blavatar_or_site_icon_url( $size, $fallback ) {
266 $image_array = Jetpack_PostImages::from_blavatar( get_the_ID(), $size );
267 if ( ! empty( $image_array ) ) {
268 return $image_array[0]['src'];
269 } else {
270 return $fallback;
271 }
272 }
273
274 /**
275 * Render a video inside a slide
276 *
277 * @param array $media - Video information.
278 *
279 * @return string
280 */
281 function render_video( $media ) {
282 if ( empty( $media['id'] ) || empty( $media['mime'] ) || empty( $media['url'] ) ) {
283 return __( 'Error retrieving media', 'jetpack' );
284 }
285
286 if ( ! empty( $media['poster'] ) ) {
287 return render_image(
288 array_merge(
289 $media,
290 array(
291 'type' => 'image',
292 'url' => $media['poster'],
293 )
294 )
295 );
296 }
297
298 return sprintf(
299 '<video
300 title="%1$s"
301 type="%2$s"
302 class="wp-story-video intrinsic-ignore wp-video-%3$s"
303 data-id="%3$d"
304 src="%4$s">
305 </video>',
306 esc_attr( get_the_title( $media['id'] ) ),
307 esc_attr( $media['mime'] ),
308 absint( $media['id'] ),
309 esc_attr( $media['url'] )
310 );
311 }
312
313 /**
314 * Pick a thumbnail to render a static/embedded story
315 *
316 * @param array $media_files - list of Media files.
317 *
318 * @return string
319 */
320 function render_static_slide( $media_files ) {
321 $media_template = '';
322 if ( empty( $media_files ) ) {
323 return '';
324 }
325
326 // find an image to showcase.
327 foreach ( $media_files as $media ) {
328 switch ( $media['type'] ) {
329 case 'image':
330 $media_template = render_image( $media );
331 break 2;
332 case 'video':
333 // ignore videos without a poster image.
334 if ( empty( $media['poster'] ) ) {
335 continue 2;
336 }
337 $media_template = render_video( $media );
338 break 2;
339 }
340 }
341
342 // if no "static" media was found for the thumbnail try to render a video tag without poster.
343 if ( empty( $media_template ) && ! empty( $media_files ) ) {
344 $media_template = render_video( $media_files[0] );
345 }
346
347 return sprintf(
348 '<div class="wp-story-slide" style="display: block;">
349 <figure>%s</figure>
350 </div>',
351 $media_template
352 );
353 }
354
355 /**
356 * Render the top right icon on top of the story embed
357 *
358 * @param array $settings - The block settings.
359 *
360 * @return string
361 */
362 function render_top_right_icon( $settings ) {
363 $show_slide_count = isset( $settings['showSlideCount'] ) ? $settings['showSlideCount'] : false;
364 $slide_count = isset( $settings['slides'] ) ? count( $settings['slides'] ) : 0;
365 if ( $show_slide_count ) {
366 // Render the story block icon along with the slide count.
367 return sprintf(
368 '<div class="wp-story-embed-icon">
369 <svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24" role="img" aria-hidden="true" focusable="false">
370 <path d="M0 0h24v24H0z" fill="none"></path>
371 <path fill-rule="evenodd" clip-rule="evenodd" d="M6 3H14V17H6L6 3ZM4 3C4 1.89543 4.89543 1 6 1H14C15.1046 1 16 1.89543 16 3V17C16 18.1046 15.1046 19 14 19H6C4.89543 19 4 18.1046 4 17V3ZM18 5C19.1046 5 20 5.89543 20 7V21C20 22.1046 19.1046 23 18 23H10C8.89543 23 8 22.1046 8 21H18V5Z"></path>
372 </svg>
373 <span>%d</span>
374 </div>',
375 $slide_count
376 );
377 } else {
378 // Render the Fullscreen Gridicon.
379 return (
380 '<div class="wp-story-embed-icon-expand">
381 <svg class="gridicon gridicons-fullscreen" role="img" height="24" width="24" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24">
382 <g>
383 <path d="M21 3v6h-2V6.41l-3.29 3.3-1.42-1.42L17.59 5H15V3zM3 3v6h2V6.41l3.29 3.3 1.42-1.42L6.41 5H9V3zm18 18v-6h-2v2.59l-3.29-3.29-1.41 1.41L17.59 19H15v2zM9 21v-2H6.41l3.29-3.29-1.41-1.42L5 17.59V15H3v6z"></path>
384 </g>
385 </svg>
386 </div>'
387 );
388 }
389 }
390
391 /**
392 * Render a pagination bullet
393 *
394 * @param int $slide_index - The slide index it corresponds to.
395 * @param string $class_name - Optional css class name(s) to customize the bullet element.
396 *
397 * @return string
398 */
399 function render_pagination_bullet( $slide_index, $class_name = '' ) {
400 return sprintf(
401 '<div class="wp-story-pagination-bullet %s" aria-label="%s">
402 <div class="wp-story-pagination-bullet-bar"></div>
403 </div>',
404 esc_attr( $class_name ),
405 /* translators: %d is the slide number (1, 2, 3...) */
406 sprintf( __( 'Go to slide %d', 'jetpack' ), $slide_index )
407 );
408 }
409
410 /**
411 * Render pagination on top of the story embed
412 *
413 * @param array $settings - The block settings.
414 *
415 * @return string
416 */
417 function render_pagination( $settings ) {
418 $show_slide_count = isset( $settings['showSlideCount'] ) ? $settings['showSlideCount'] : false;
419 if ( $show_slide_count ) {
420 return '';
421 }
422 $slide_count = isset( $settings['slides'] ) ? count( $settings['slides'] ) : 0;
423 $bullet_count = min( $slide_count, MAX_BULLETS );
424 $bullet_ellipsis = $slide_count > $bullet_count
425 ? render_pagination_bullet( $bullet_count + 1, 'wp-story-pagination-ellipsis' )
426 : '';
427 return sprintf(
428 '<div class="wp-story-pagination wp-story-pagination-bullets">
429 %s
430 </div>',
431 implode( "\n", array_map( __NAMESPACE__ . '\render_pagination_bullet', range( 1, $bullet_count ) ) ) . $bullet_ellipsis
432 );
433 }
434
435 /**
436 * Render story block
437 *
438 * @param array $attributes - Block attributes.
439 *
440 * @return string
441 */
442 function render_block( $attributes ) {
443 // Let's use a counter to have a different id for each story rendered in the same context.
444 static $story_block_counter = 0;
445
446 if ( 0 === $story_block_counter ) {
447 // @todo Fix the webpack tree shaking so the block's view.js no longer depends on jetpack-connection, then remove this.
448 Connection_Assets::register_assets();
449 }
450
451 Jetpack_Gutenberg::load_assets_as_required( __DIR__ );
452
453 $media_files = isset( $attributes['mediaFiles'] ) ? enrich_media_files( $attributes['mediaFiles'] ) : array();
454 $settings_from_attributes = isset( $attributes['settings'] ) ? $attributes['settings'] : array();
455
456 $settings = array_merge(
457 $settings_from_attributes,
458 array(
459 'slides' => $media_files,
460 )
461 );
462
463 return sprintf(
464 '<div class="%1$s" data-id="%2$s" data-settings="%3$s">
465 <div class="wp-story-app">
466 <div class="wp-story-display-contents" style="display: contents;">
467 <a class="wp-story-container" href="%4$s" title="%5$s">
468 <div class="wp-story-meta">
469 <div class="wp-story-icon">
470 <img alt="%6$s" src="%7$s" width="40" height="40">
471 </div>
472 <div>
473 <div class="wp-story-title">
474 %8$s
475 </div>
476 </div>
477 </div>
478 <div class="wp-story-wrapper">
479 %9$s
480 </div>
481 <div class="wp-story-overlay">
482 %10$s
483 </div>
484 %11$s
485 </a>
486 </div>
487 </div>
488 </div>',
489 esc_attr( Blocks::classes( Blocks::get_block_feature( __DIR__ ), $attributes, array( 'wp-story', 'aligncenter' ) ) ),
490 esc_attr( 'wp-story-' . get_the_ID() . '-' . strval( ++$story_block_counter ) ),
491 filter_var( wp_json_encode( $settings ), FILTER_SANITIZE_SPECIAL_CHARS ),
492 get_permalink() . '?wp-story-load-in-fullscreen=true&amp;wp-story-play-on-load=true',
493 __( 'Play story in new tab', 'jetpack' ),
494 __( 'Site icon', 'jetpack' ),
495 esc_attr( get_blavatar_or_site_icon_url( 80, includes_url( 'images/w-logo-blue.png' ) ) ),
496 esc_html( get_the_title() ),
497 render_static_slide( $media_files ),
498 render_top_right_icon( $settings ),
499 render_pagination( $settings )
500 );
501 }
502