Code Coverage
 
Lines
Functions and Methods
Classes and Traits
Total
0.00% covered (danger)
0.00%
0 / 107
0.00% covered (danger)
0.00%
0 / 4
CRAP
n/a
0 / 0
jetpack_featured_images_fallback_get_image
0.00% covered (danger)
0.00%
0 / 63
0.00% covered (danger)
0.00%
0 / 1
650
jetpack_featured_images_fallback_get_image_src
0.00% covered (danger)
0.00%
0 / 29
0.00% covered (danger)
0.00%
0 / 1
240
jetpack_has_featured_image
0.00% covered (danger)
0.00%
0 / 1
0.00% covered (danger)
0.00%
0 / 1
2
jetpack_featured_images_post_class
0.00% covered (danger)
0.00%
0 / 6
0.00% covered (danger)
0.00%
0 / 1
42
1<?php
2/**
3 * Theme Tools: functions for Featured Images fallback.
4 *
5 * @package automattic/jetpack-classic-theme-helper
6 */
7
8use Automattic\Jetpack\Post_Media\Images;
9
10if ( ! defined( 'ABSPATH' ) ) {
11    exit( 0 );
12}
13
14if ( ! function_exists( 'jetpack_featured_images_fallback_get_image' ) ) {
15
16    /**
17     * Get one image from a specified post in the following order:
18     * Featured Image then first image from the_content HTML
19     * and filter the post_thumbnail_html
20     *
21     * @param string       $html              The HTML for the image markup.
22     * @param int          $post_id           The post ID to check.
23     * @param int          $post_thumbnail_id The ID of the featured image.
24     * @param string       $size              The image size to return, defaults to 'post-thumbnail'.
25     * @param string|array $attr              Optional. Query string or array of attributes.
26     *
27     * @return string      $html              Thumbnail image with markup.
28     */
29    function jetpack_featured_images_fallback_get_image( $html, $post_id, $post_thumbnail_id, $size, $attr ) {
30        $opts = jetpack_featured_images_get_settings();
31
32        if ( ! empty( $html ) || (bool) 1 !== (bool) $opts['fallback-option'] ) {
33            return trim( $html );
34        }
35
36        if ( jetpack_featured_images_should_load() ) {
37            if (
38                ( true === $opts['archive'] && ( is_home() || is_archive() || is_search() ) && ! $opts['archive-option'] )
39                || ( true === $opts['post'] && is_single() && ! $opts['post-option'] )
40                || ! $opts['fallback-option']
41            ) {
42                return trim( $html );
43            }
44        }
45
46        global $_wp_additional_image_sizes;
47
48        $args = array(
49            'from_thumbnail'  => false,
50            'from_slideshow'  => true,
51            'from_gallery'    => true,
52            'from_attachment' => false,
53        );
54
55        $image = Images::get_image( $post_id, $args );
56
57        if ( ! empty( $image ) ) {
58            $image['width']  = '';
59            $image['height'] = '';
60            $image['crop']   = '';
61
62            if ( array_key_exists( $size, $_wp_additional_image_sizes ) ) {
63                $image['width']  = $_wp_additional_image_sizes[ $size ]['width'];
64                $image['height'] = $_wp_additional_image_sizes[ $size ]['height'];
65                $image['crop']   = $_wp_additional_image_sizes[ $size ]['crop'];
66            }
67
68            // Force `crop` to be a simple boolean, to avoid dealing with WP crop positions.
69            $image['crop'] = boolval( $image['crop'] );
70
71            $image_sizes = '';
72
73            $width  = isset( $image['width'] ) ? intval( $image['width'] ) : null;
74            $height = isset( $image['height'] ) ? intval( $image['height'] ) : null;
75
76            if ( ! empty( $image['src_width'] ) && ! empty( $image['src_height'] ) && ! empty( $image['width'] ) && ! empty( $image['height'] ) ) {
77                $src_width  = intval( $image['src_width'] );
78                $src_height = intval( $image['src_height'] );
79
80                // If we're aware of the source dimensions, calculate the final image height and width.
81                // This allows us to provide them as attributes in the `<img>` tag, to avoid layout shifts.
82                // It also allows us to calculate a `srcset`.
83                if ( $image['crop'] ) {
84                    // If we're cropping, the final dimensions are calculated independently of each other.
85                    $width  = min( $width, $src_width );
86                    $height = min( $height, $src_height );
87                } else {
88                    // If we're not cropping, we need to preserve aspect ratio.
89                    $dims   = wp_constrain_dimensions( $src_width, $src_height, $width, $height );
90                    $width  = $dims[0];
91                    $height = $dims[1];
92                }
93
94                $image_src    = Images::fit_image_url( $image['src'], $width, $height );
95                $image_srcset = Images::generate_cropped_srcset( $image, $width, $height, true );
96                $image_sizes  = 'min(' . $width . 'px, 100vw)';
97            } else {
98                // If we're not aware of the source dimensions, leave the size calculations to the CDN, and
99                // fall back to a simpler `<img>` tag without `width`/`height` or `srcset`.
100                $image_src = Images::fit_image_url( $image['src'], $image['width'], $image['height'] );
101
102                // Use the theme's crop setting rather than forcing to true.
103                $image_src = add_query_arg( 'crop', $image['crop'], $image_src );
104
105                $image_srcset = null;
106                $image_sizes  = null;
107            }
108
109            $default_attr = array(
110                'srcset'   => $image_srcset,
111                'sizes'    => $image_sizes,
112                'loading'  => is_singular() ? 'eager' : 'lazy',
113                'decoding' => 'async',
114                'title'    => wp_strip_all_tags( get_the_title() ),
115                'alt'      => '',
116                'class'    => "attachment-$size wp-post-image",
117            );
118
119            $image_attr = wp_parse_args( $attr, $default_attr );
120            $hwstring   = image_hwstring( $width, $height );
121
122            $html  = rtrim( "<img $hwstring" );
123            $html .= ' src="' . esc_url( $image_src ) . '"';
124
125            foreach ( $image_attr as $name => $value ) {
126                if ( $value ) {
127                    $html .= " $name=" . '"' . esc_attr( $value ) . '"';
128                }
129            }
130
131            $html .= ' />';
132
133            return trim( $html );
134        }
135
136        return trim( $html );
137    }
138    add_filter( 'post_thumbnail_html', 'jetpack_featured_images_fallback_get_image', 10, 5 );
139
140}
141
142if ( ! function_exists( 'jetpack_featured_images_fallback_get_image_src' ) ) {
143
144    /**
145     * Get URL of one image from a specified post in the following order:
146     * Featured Image then first image from the_content HTML
147     *
148     * @param int    $post_id           The post ID to check.
149     * @param int    $post_thumbnail_id The ID of the featured image.
150     * @param string $size              The image size to return, defaults to 'post-thumbnail'.
151     *
152     * @return string|null $image_src         The URL of the thumbnail image.
153     */
154    function jetpack_featured_images_fallback_get_image_src( $post_id, $post_thumbnail_id, $size ) {
155        $image_src = wp_get_attachment_image_src( $post_thumbnail_id, $size );
156        $image_src = ( ! empty( $image_src[0] ) ) ? $image_src[0] : '';
157        $opts      = jetpack_featured_images_get_settings();
158
159        if ( ! empty( $image_src ) || (bool) 1 !== (bool) $opts['fallback-option'] ) {
160            return esc_url( $image_src );
161        }
162
163        if ( jetpack_featured_images_should_load() ) {
164            if ( ( true === $opts['archive'] && ( is_home() || is_archive() || is_search() ) && ! $opts['archive-option'] )
165                || ( true === $opts['post'] && is_single() && ! $opts['post-option'] ) ) {
166                    return esc_url( $image_src );
167            }
168        }
169
170        global $_wp_additional_image_sizes;
171
172        $args = array(
173            'from_thumbnail'  => false,
174            'from_slideshow'  => true,
175            'from_gallery'    => true,
176            'from_attachment' => false,
177        );
178
179        $image = Images::get_image( $post_id, $args );
180
181        if ( ! empty( $image ) ) {
182            $image['width']  = '';
183            $image['height'] = '';
184            $image['crop']   = '';
185
186            if ( array_key_exists( $size, $_wp_additional_image_sizes ) ) {
187                $image['width']  = $_wp_additional_image_sizes[ $size ]['width'];
188                $image['height'] = $_wp_additional_image_sizes[ $size ]['height'];
189                $image['crop']   = $_wp_additional_image_sizes[ $size ]['crop'];
190            }
191
192            $image_src = Images::fit_image_url( $image['src'], $image['width'], $image['height'] );
193
194            // Use the theme's crop setting rather than forcing to true.
195            $image_src = add_query_arg( 'crop', $image['crop'], $image_src );
196
197            return esc_url( $image_src );
198        }
199
200        return esc_url( $image_src );
201    }
202
203}
204
205if ( ! function_exists( 'jetpack_has_featured_image' ) ) {
206
207    /**
208     * Check if post has an image attached, including a fallback.
209     *
210     * @param  int $post The post ID to check.
211     *
212     * @return bool
213     */
214    function jetpack_has_featured_image( $post = null ) {
215        return (bool) get_the_post_thumbnail( $post );
216    }
217
218}
219
220if ( ! function_exists( 'jetpack_featured_images_post_class' ) ) {
221
222    /**
223     * Adds custom class to the array of post classes.
224     *
225     * @param array $classes Classes for the post element.
226     * @param array $class   Optional. Comma-separated list of additional classes.
227     * @param int   $post_id Unique The post ID to check.
228     *
229     * @return array $classes
230     */
231    function jetpack_featured_images_post_class( $classes, $class, $post_id ) {
232        $post_password_required = post_password_required( $post_id );
233        $opts                   = jetpack_featured_images_get_settings();
234
235        if ( jetpack_has_featured_image( $post_id ) && (bool) 1 === (bool) $opts['fallback-option'] && ! is_attachment() && ! $post_password_required && 'post' === get_post_type() ) {
236            $classes[] = 'has-post-thumbnail';
237            $classes[] = 'fallback-thumbnail';
238        }
239
240        return $classes;
241    }
242    add_filter( 'post_class', 'jetpack_featured_images_post_class', 10, 3 );
243
244}