Code Coverage
 
Lines
Functions and Methods
Classes and Traits
Total
0.00% covered (danger)
0.00%
0 / 316
0.00% covered (danger)
0.00%
0 / 3
CRAP
n/a
0 / 0
newspack_blocks_render_block_carousel
0.00% covered (danger)
0.00%
0 / 198
0.00% covered (danger)
0.00%
0 / 1
3306
newspack_blocks_carousel_block_autoplay_ui
0.00% covered (danger)
0.00%
0 / 5
0.00% covered (danger)
0.00%
0 / 1
2
newspack_blocks_register_carousel
0.00% covered (danger)
0.00%
0 / 112
0.00% covered (danger)
0.00%
0 / 1
2
1<?php
2/**
3 * Server-side rendering of the `newspack-blocks/carousel` block.
4 *
5 * @package WordPress
6 */
7
8/**
9 * Renders the `newspack-blocks/carousel` block on server.
10 *
11 * @param array $attributes The block attributes.
12 *
13 * @return string Returns the post content with latest posts added.
14 */
15function newspack_blocks_render_block_carousel( $attributes ) {
16
17    // Don't output the block inside RSS feeds.
18    if ( is_feed() ) {
19        return;
20    }
21
22    static $newspack_blocks_carousel_id = 0;
23    global $newspack_blocks_post_id;
24
25    // This will let the FSE plugin know we need CSS/JS now.
26    do_action( 'newspack_blocks_render_post_carousel' );
27
28    $newspack_blocks_carousel_id++;
29    $autoplay = isset( $attributes['autoplay'] ) ? $attributes['autoplay'] : false;
30    $delay    = isset( $attributes['delay'] ) ? absint( $attributes['delay'] ) : 3;
31    $authors  = isset( $attributes['authors'] ) ? $attributes['authors'] : array();
32
33    $other = array();
34    if ( $autoplay ) {
35        $other[] = 'wp-block-newspack-blocks-carousel__autoplay-playing';
36    }
37    $other[] = 'slides-per-view-' . $attributes['slidesPerView'];
38    $other[] = 'wpnbpc';
39    $classes = Newspack_Blocks::block_classes( 'carousel', $attributes, $other );
40
41    $article_query = new WP_Query( Newspack_Blocks::build_articles_query( $attributes, apply_filters( 'newspack_blocks_block_name', 'newspack-blocks/carousel' ) ) );
42    if ( false === $article_query->have_posts() ) {
43        return '';
44    }
45
46    $counter = 0;
47
48    ob_start();
49    if ( $article_query->have_posts() ) :
50        while ( $article_query->have_posts() ) :
51            $article_query->the_post();
52            $post_id                             = get_the_ID();
53            $authors                             = Newspack_Blocks::prepare_authors();
54            if ( Newspack_Blocks::should_deduplicate_block( $attributes ) ) {
55                $newspack_blocks_post_id[ $post_id ] = true;
56            }
57
58            $article_classes = [
59                'post-has-image',
60                'swiper-slide',
61            ];
62
63            // Add classes based on the post's assigned categories and tags.
64            $article_classes[] = Newspack_Blocks::get_term_classes( $post_id );
65
66            // Get sponsors for this post.
67            $sponsors = Newspack_Blocks::get_all_sponsors( $post_id );
68
69            $counter++;
70            $has_featured_image = has_post_thumbnail();
71            $post_type          = get_post_type();
72            $post_link          = Newspack_Blocks::get_post_link( $post_id );
73
74            // Support Newspack Listings hide author/publish date options.
75            $hide_author       = apply_filters( 'newspack_listings_hide_author', false ); // phpcs:ignore WordPress.NamingConventions.PrefixAllGlobals.NonPrefixedHooknameFound
76            $hide_publish_date = apply_filters( 'newspack_listings_hide_publish_date', false ); // phpcs:ignore WordPress.NamingConventions.PrefixAllGlobals.NonPrefixedHooknameFound
77            $show_author       = $attributes['showAuthor'] && ! $hide_author;
78            $show_date         = $attributes['showDate'] && ! $hide_publish_date;
79            $show_caption      = $attributes['showCaption'];
80            $show_credit       = $attributes['showCredit'];
81
82            // Validate the value of the "image fit" attribute.
83            $image_fits = [ 'cover', 'contain' ];
84            $image_fit  = in_array( $attributes['imageFit'], $image_fits, true ) ? $attributes['imageFit'] : $image_fits[0];
85            ?>
86
87            <article data-post-id="<?php echo esc_attr( $post_id ); ?>" class="<?php echo esc_attr( implode( ' ', $article_classes ) . ' ' . $post_type ); ?>">
88                <?php echo Newspack_Blocks::get_post_status_label(); // phpcs:ignore WordPress.Security.EscapeOutput.OutputNotEscaped ?>
89                <figure class="post-thumbnail">
90                    <?php if ( $post_link ) : ?>
91                    <a href="<?php echo esc_url( $post_link ); ?>" rel="bookmark" tabindex="-1" aria-hidden="true">
92                    <?php endif; ?>
93                        <?php if ( $has_featured_image ) : ?>
94                            <?php
95                                the_post_thumbnail(
96                                    'large',
97                                    array(
98                                        'object-fit' => $image_fit,
99                                        'layout'     => 'fill',
100                                        'class'      => 'contain' === $image_fit ? 'image-fit-contain' : 'image-fit-cover',
101                                        'alt'        => trim( wp_strip_all_tags( get_the_title( $post_id ) ) ),
102                                    )
103                                );
104                            ?>
105                        <?php else : ?>
106                            <div class="wp-block-newspack-blocks-carousel__placeholder"></div>
107                        <?php endif; ?>
108                    <?php if ( $post_link ) : ?>
109                    </a>
110                    <?php endif; ?>
111                </figure>
112
113                <?php if ( ! empty( $sponsors ) || $attributes['showCategory'] || $attributes['showTitle'] || $show_author || $show_date || $show_caption || $show_credit ) : ?>
114                    <div class="entry-wrapper">
115                        <?php if ( ! empty( $sponsors ) || $attributes['showCategory'] ) : ?>
116                            <div class="cat-links <?php if ( ! empty( $sponsors ) ) : ?>sponsor-label<?php endif; // phpcs:ignore Squiz.ControlStructures.ControlSignature.NewlineAfterOpenBrace ?>">
117                                <?php if ( ! empty( $sponsors ) ) : ?>
118                                    <span class="flag">
119                                        <?php echo esc_html( Newspack_Blocks::get_sponsor_label( $sponsors ) ); ?>
120                                    </span>
121                                    <?php
122                                endif;
123
124                                if ( $attributes['showCategory'] && ( empty( $sponsors ) || Newspack_Blocks::newspack_display_sponsors_and_categories( $sponsors ) ) ) :
125                                    echo wp_kses_post( newspack_blocks_format_categories( $post_id ) );
126                                endif;
127                                ?>
128                                </div>
129                            <?php
130                        endif;
131
132                        if ( $attributes['showTitle'] ) {
133                            the_title( '<h3 class="entry-title"><a href="' . esc_url( $post_link ) . '" rel="bookmark">', '</a></h3>' );
134                        }
135                        ?>
136
137                        <div class="entry-meta">
138                            <?php
139                            if ( ! empty( $sponsors ) ) :
140                                $sponsor_classes = [ 'entry-sponsors' ];
141                                if ( Newspack_Blocks::newspack_display_sponsors_and_authors( $sponsors ) ) {
142                                    $sponsor_classes[] = 'plus-author';
143                                }
144                                ?>
145                                <span class="<?php echo esc_attr( implode( ' ', $sponsor_classes ) ); ?>">
146                                    <?php
147                                    $logos = Newspack_Blocks::get_sponsor_logos( $sponsors );
148                                    if ( ! empty( $logos ) ) :
149                                        ?>
150                                    <span class="sponsor-logos">
151                                        <?php
152                                        foreach ( $logos as $logo ) {
153                                            if ( '' !== $logo['url'] ) {
154                                                echo '<a href="' . esc_url( $logo['url'] ) . '" target="_blank">';
155                                            }
156                                            echo '<img src="' . esc_url( $logo['src'] ) . '" alt="' . esc_attr( $logo['alt'] ) . '" width="' . esc_attr( $logo['width'] ) . '" height="' . esc_attr( $logo['height'] ) . '">';
157                                            if ( '' !== $logo['url'] ) {
158                                                echo '</a>';
159                                            }
160                                        }
161                                        ?>
162                                    </span>
163                                    <?php endif; ?>
164
165                                    <span class="byline sponsor-byline">
166                                        <?php
167                                        $bylines = Newspack_Blocks::get_sponsor_byline( $sponsors );
168                                        echo esc_html( $bylines[0]['byline'] ) . ' ';
169                                        foreach ( $bylines as $byline ) {
170                                            echo '<span class="author">';
171                                            if ( '' !== $byline['url'] ) {
172                                                echo '<a target="_blank" href="' . esc_url( $byline['url'] ) . '">';
173                                            }
174                                            echo esc_html( $byline['name'] );
175                                            if ( '' !== $byline['url'] ) {
176                                                echo '</a>';
177                                            }
178                                            echo '</span>' . esc_html( $byline['sep'] );
179                                        }
180                                        ?>
181                                    </span>
182                                </span><!-- .entry-sponsors -->
183                                <?php
184                            endif;
185
186                            if ( $show_author && ( empty( $sponsors ) || Newspack_Blocks::newspack_display_sponsors_and_authors( $sponsors ) ) ) :
187                                if ( $attributes['showAvatar'] ) :
188                                    echo wp_kses(
189                                        newspack_blocks_format_avatars( $authors ),
190                                        Newspack_Blocks::get_sanitized_image_attributes()
191                                    );
192                                endif;
193                                ?>
194                                <span class="byline">
195                                    <?php echo wp_kses_post( newspack_blocks_format_byline( $authors ) ); ?>
196                                </span><!-- .author-name -->
197                                <?php
198                            endif;
199                            if ( $show_date ) :
200                                printf(
201                                    '<time class="entry-date published" datetime="%1$s">%2$s</time>',
202                                    esc_attr( get_the_date( DATE_W3C ) ),
203                                    esc_html( get_the_date() )
204                                );
205                            endif;
206                            if ( $show_caption || $show_credit ) :
207                                $full_caption = Newspack_Blocks::get_image_caption( get_post_thumbnail_id(), $show_caption, $show_credit );
208                                if ( $full_caption ) :
209                                    ?>
210                                    <div class="entry-caption">
211                                        <?php echo wp_kses_post( $full_caption ); ?>
212                                    <?php
213                                endif;
214                            endif;
215                            ?>
216                        </div><!-- .entry-meta -->
217                    </div><!-- .entry-wrapper -->
218                <?php endif; ?>
219            </article>
220            <?php
221        endwhile;
222        endif;
223        wp_reset_postdata();
224    $slides  = ob_get_clean();
225    $buttons = array();
226    for ( $x = 0; $x < $counter; $x++ ) {
227        $aria_label = sprintf(
228            /* translators: %d: Slide number. */
229            __( 'Go to slide %d', 'jetpack-mu-wpcom' ),
230            absint( $x + 1 )
231        );
232        $buttons[] = sprintf(
233            '<button option="%d" class="swiper-pagination-bullet" aria-label="%s" %s></button>',
234            absint( $x ),
235            esc_attr( $aria_label ),
236            0 === $x ? 'selected' : ''
237        );
238    }
239
240    $slides_per_view = absint( $attributes['slidesPerView'] ?? 1 );
241    $aspect_ratio    = floatval( $attributes['aspectRatio'] ?? 0.75 );
242
243    $selector    = sprintf(
244        '<div class="swiper-pagination-bullets" %1$s>%2$s</div>',
245        $attributes['hideControls'] ? 'aria-hidden="true"' : '',
246        implode( '', $buttons )
247    );
248    $navigation  = 1 === $counter ? '' : sprintf(
249        '<button class="swiper-button swiper-button-prev" aria-label="%1$s" %3$s></button><button class="swiper-button swiper-button-next" aria-label="%2$s" %3$s></button>',
250        esc_attr__( 'Previous Slide', 'jetpack-mu-wpcom' ),
251        esc_attr__( 'Next Slide', 'jetpack-mu-wpcom' ),
252        $attributes['hideControls'] ? 'aria-hidden="true"' : ''
253    );
254    $carousel    = sprintf(
255        '<div class="swiper"><div class="swiper-wrapper">%s</div>%s</div>',
256        $slides,
257        $navigation
258    );
259    $autoplay_ui = $autoplay ? newspack_blocks_carousel_block_autoplay_ui( $newspack_blocks_carousel_id ) : '';
260
261    $data_attributes = [
262        'data-current-post-id=' . $post_id,
263        'data-slides-per-view=' . esc_attr( $slides_per_view ),
264        'data-slide-count=' . $counter,
265        'data-aspect-ratio=' . esc_attr( $aspect_ratio ),
266    ];
267
268    if ( $autoplay ) {
269        $data_attributes[] = 'data-autoplay=1';
270        $data_attributes[] = sprintf( 'data-autoplay_delay=%s', esc_attr( $delay ) );
271    }
272    Newspack_Blocks::enqueue_view_assets( 'carousel', 'defer' );
273    if ( 1 === $counter ) {
274        $selector = '';
275    }
276    return sprintf(
277        '<div class="%1$s" id="wp-block-newspack-carousel__%2$d" %3$s>%4$s%5$s%6$s</div>',
278        esc_attr( $classes ),
279        absint( $newspack_blocks_carousel_id ),
280        esc_attr( implode( ' ', $data_attributes ) ),
281        $autoplay_ui,
282        $carousel,
283        $selector
284    );
285}
286
287/**
288 * Generate autoplay play/pause UI.
289 *
290 * @return string Autoplay UI markup.
291 */
292function newspack_blocks_carousel_block_autoplay_ui() {
293    return sprintf(
294        '<button aria-label="%s" class="swiper-button swiper-button-pause"></button><button aria-label="%s" class="swiper-button swiper-button-play"></button>',
295        esc_attr__( 'Pause Slideshow', 'jetpack-mu-wpcom' ),
296        esc_attr__( 'Play Slideshow', 'jetpack-mu-wpcom' )
297    );
298}
299
300/**
301 * Registers the `newspack-blocks/carousel` block on server.
302 */
303function newspack_blocks_register_carousel() {
304    register_block_type(
305        apply_filters( 'newspack_blocks_block_name', 'newspack-blocks/carousel' ),
306        apply_filters(
307            'newspack_blocks_block_args',
308            array(
309                'api_version'     => 3,
310                'attributes'      => array(
311                    'className'        => array(
312                        'type' => 'string',
313                    ),
314                    'postsToShow'      => array(
315                        'type'    => 'integer',
316                        'default' => 3,
317                    ),
318                    'authors'          => array(
319                        'type'    => 'array',
320                        'default' => array(),
321                        'items'   => array(
322                            'type' => 'integer',
323                        ),
324                    ),
325                    'categories'       => array(
326                        'type'    => 'array',
327                        'default' => array(),
328                        'items'   => array(
329                            'type' => 'integer',
330                        ),
331                    ),
332                    'tags'             => array(
333                        'type'    => 'array',
334                        'default' => array(),
335                        'items'   => array(
336                            'type' => 'integer',
337                        ),
338                    ),
339                    'customTaxonomies' => array(
340                        'type'    => 'array',
341                        'default' => array(),
342                        'items'   => array(
343                            'type'       => 'object',
344                            'properties' => array(
345                                'slug'  => array(
346                                    'type' => 'string',
347                                ),
348                                'terms' => array(
349                                    'type'  => 'array',
350                                    'items' => array(
351                                        'type' => 'integer',
352                                    ),
353                                ),
354                            ),
355                        ),
356                    ),
357                    'autoplay'         => array(
358                        'type'    => 'boolean',
359                        'default' => false,
360                    ),
361                    'delay'            => array(
362                        'type'    => 'integer',
363                        'default' => 5,
364                    ),
365                    'showAuthor'       => array(
366                        'type'    => 'boolean',
367                        'default' => true,
368                    ),
369                    'showAvatar'       => array(
370                        'type'    => 'boolean',
371                        'default' => true,
372                    ),
373                    'showCaption'      => array(
374                        'type'    => 'boolean',
375                        'default' => false,
376                    ),
377                    'showCredit'       => array(
378                        'type'    => 'boolean',
379                        'default' => false,
380                    ),
381                    'showCategory'     => array(
382                        'type'    => 'boolean',
383                        'default' => false,
384                    ),
385                    'showDate'         => array(
386                        'type'    => 'boolean',
387                        'default' => true,
388                    ),
389                    'imageFit'         => array(
390                        'type'    => 'string',
391                        'default' => 'cover',
392                    ),
393                    'showTitle'        => array(
394                        'type'    => 'boolean',
395                        'default' => true,
396                    ),
397                    'slidesPerView'    => array(
398                        'type'    => 'number',
399                        'default' => 1,
400                    ),
401                    'hideControls'     => array(
402                        'type'    => 'boolean',
403                        'default' => false,
404                    ),
405                    'aspectRatio'      => array(
406                        'type'    => 'number',
407                        'default' => 0.75,
408                    ),
409                ),
410                'render_callback' => 'newspack_blocks_render_block_carousel',
411                'supports'        => [],
412            ),
413            'carousel'
414        )
415    );
416}
417add_action( 'init', 'newspack_blocks_register_carousel' );