Code Coverage
 
Lines
Functions and Methods
Classes and Traits
Total
0.00% covered (danger)
0.00%
0 / 32
0.00% covered (danger)
0.00%
0 / 3
CRAP
0.00% covered (danger)
0.00%
0 / 1
Images
0.00% covered (danger)
0.00%
0 / 32
0.00% covered (danger)
0.00%
0 / 3
272
0.00% covered (danger)
0.00%
0 / 1
 is_enabled
0.00% covered (danger)
0.00%
0 / 1
0.00% covered (danger)
0.00%
0 / 1
6
 block
0.00% covered (danger)
0.00%
0 / 26
0.00% covered (danger)
0.00%
0 / 1
110
 render_core
0.00% covered (danger)
0.00%
0 / 5
0.00% covered (danger)
0.00%
0 / 1
20
1<?php
2/**
3 * Images in comments.
4 *
5 * @package automattic/jetpack-comments
6 */
7
8namespace Automattic\Jetpack\Comments;
9
10/**
11 * The image block in a comment: a URL a commenter gave, never an upload. The stored comment holds the
12 * URL as text, so nothing that prints it raw loads it.
13 */
14class Images {
15
16    /**
17     * Whether the editor offers the image block, and comments draw the ones they hold.
18     *
19     * @return bool
20     */
21    public static function is_enabled() {
22        /**
23         * Offer the image block in the Jetpack Comments editor. Off, comments show their images as links.
24         *
25         * @since 0.6.0
26         *
27         * @param bool $enabled Whether to offer it. Default true, wherever the block editor is on.
28         */
29        return Block_Editor::is_enabled() && (bool) apply_filters( 'jetpack_comments_block_editor_images', true );
30    }
31
32    /**
33     * The image block as this package writes it: the URL in its attributes, and as the figure's text.
34     * An http image, one past the comment's limit, or any where images are off or the comment may not
35     * draw them is a paragraph holding the link. At render, the figure draws the image.
36     *
37     * @param array     $block  Parsed image block.
38     * @param bool|null $render Null while saving; at render, whether the comment may show the image.
39     * @param int       $count  Images the comment has drawn so far, counted up here.
40     * @return array|null The block to keep, if any.
41     */
42    public static function block( array $block, $render, &$count ) {
43        // This editor keeps the URL in the attributes; Verbum's kept it on an img.
44        $tags = new \WP_HTML_Tag_Processor( $block['innerHTML'] );
45        $url  = $block['attrs']['url'] ?? ( $tags->next_tag( 'img' ) ? $tags->get_attribute( 'src' ) : null );
46
47        // kses writes & as &amp; in the block's attributes.
48        $url = is_string( $url ) ? trim( html_entity_decode( $url, ENT_QUOTES ) ) : '';
49        $url = strlen( $url ) > 2048 ? '' : esc_url_raw( $url, array( 'http', 'https' ) );
50
51        if ( ! $url ) {
52            return null;
53        }
54
55        $link = Block_Editor::paragraph( '<a href="' . esc_url( $url ) . '" rel="nofollow ugc">' . esc_html( $url ) . '</a>' );
56
57        // An http image would be mixed content on the https page. Checked before the count, which only
58        // images that draw use up: four a comment at most.
59        if ( ! self::is_enabled() || false === $render || 'https' !== wp_parse_url( $url, PHP_URL_SCHEME ) || ++$count > 4 ) {
60            return $link;
61        }
62
63        if ( null === $render ) {
64            $block['attrs'] = array( 'url' => $url );
65            $html           = '<figure class="wp-block-image">' . esc_html( $url ) . '</figure>';
66        } else {
67            wp_enqueue_style( 'wp-block-image' );
68
69            $img = new \WP_HTML_Tag_Processor( '<img>' );
70            $img->next_tag();
71            $img->set_attribute( 'src', $url );
72            $img->set_attribute( 'loading', 'lazy' );
73            $img->set_attribute( 'decoding', 'async' );
74            $img->set_attribute( 'referrerpolicy', 'no-referrer' );
75            $img->set_attribute( 'style', 'max-width:100%;height:auto' );
76
77            $block['attrs'] = array();
78            $html           = '<figure class="wp-block-image">' . $img->get_updated_html() . '</figure>';
79        }
80
81        $block['innerBlocks']  = array();
82        $block['innerHTML']    = $html;
83        $block['innerContent'] = array( $html );
84
85        return $block;
86    }
87
88    /**
89     * The link, where a caller runs the comment through do_blocks() before `comment_text`, as
90     * WordPress.com's notifications do: core's own render draws nothing for a figure without an img.
91     *
92     * @param string $content What core drew.
93     * @param array  $block   Parsed block.
94     * @return string
95     */
96    public static function render_core( $content, $block ) {
97        if ( '' !== $content || ! isset( $block['attrs']['url'] ) ) {
98            return $content;
99        }
100
101        $count = 0;
102        $link  = self::block( $block, false, $count );
103
104        return $link ? $link['innerHTML'] : $content;
105    }
106}