Code Coverage
 
Lines
Functions and Methods
Classes and Traits
Total
88.00% covered (warning)
88.00%
22 / 25
25.00% covered (danger)
25.00%
1 / 4
CRAP
0.00% covered (danger)
0.00%
0 / 1
Share_Image
88.00% covered (warning)
88.00%
22 / 25
25.00% covered (danger)
25.00%
1 / 4
22.84
0.00% covered (danger)
0.00%
0 / 1
 init
0.00% covered (danger)
0.00%
0 / 1
0.00% covered (danger)
0.00%
0 / 1
2
 validate_request
100.00% covered (success)
100.00%
8 / 8
100.00% covered (success)
100.00%
1 / 1
10
 image
92.31% covered (success)
92.31%
12 / 13
0.00% covered (danger)
0.00%
0 / 1
8.03
 fallback_image
66.67% covered (warning)
66.67%
2 / 3
0.00% covered (danger)
0.00%
0 / 1
3.33
1<?php
2/**
3 * Default image for social cards without a content-specific image.
4 *
5 * @package automattic/jetpack-seo
6 */
7
8namespace Automattic\Jetpack\SEO;
9
10use WP_Error;
11use WP_REST_Request;
12
13/**
14 * Supplies an owner-selected image through the existing social fallback.
15 *
16 * @since $$next-version$$
17 */
18class Share_Image {
19    /**
20     * Option containing the attachment ID.
21     *
22     * @var string
23     */
24    const OPTION = 'jetpack_seo_default_share_image';
25
26    /** Register the fallback without changing Social's content-specific images. */
27    public static function init() {
28        add_filter( 'jetpack_og_default_site_image', array( __CLASS__, 'fallback_image' ), 20 );
29    }
30
31    /**
32     * Reject invalid media choices before core updates any settings.
33     *
34     * @param mixed           $response Previous response.
35     * @param array           $handler Route handler.
36     * @param WP_REST_Request $request Settings request.
37     * @return mixed
38     */
39    public static function validate_request( $response, $handler, WP_REST_Request $request ) {
40        if ( null !== $response || ! current_user_can( 'manage_options' ) || ! preg_match( '#^/wp/v2/(?:sites/[^/]+/)?settings$#', $request->get_route() ) || ! in_array( $request->get_method(), array( 'POST', 'PUT', 'PATCH' ), true ) || ! $request->has_param( self::OPTION ) ) {
41            return $response;
42        }
43        $value = $request->get_param( self::OPTION );
44        if ( 0 === $value ) {
45            return $response;
46        }
47        if ( ! current_user_can( 'upload_files' ) || ! current_user_can( 'edit_post', $value ) || ! self::image( $value ) ) {
48            return new WP_Error( 'jetpack_seo_invalid_share_image', __( 'Choose a JPEG, PNG, GIF or WebP image from the Media Library.', 'jetpack-seo' ), array( 'status' => 400 ) );
49        }
50        return $response;
51    }
52
53    /**
54     * Resolve a supported attachment on the current site.
55     *
56     * @param int $id Attachment ID.
57     * @return array|null
58     */
59    public static function image( $id ) {
60        if ( ! $id || 'attachment' !== get_post_type( $id ) || 'trash' === get_post_status( $id ) || ! in_array( get_post_mime_type( $id ), array( 'image/jpeg', 'image/png', 'image/gif', 'image/webp' ), true ) ) {
61            return null;
62        }
63        $image = wp_get_attachment_image_src( $id, 'full' );
64        if ( ! $image || empty( $image[1] ) || empty( $image[2] ) ) {
65            return null;
66        }
67        return array(
68            'type'     => 'seo_default',
69            'id'       => (int) $id,
70            'src'      => $image[0],
71            'width'    => $image[1],
72            'height'   => $image[2],
73            'alt_text' => (string) get_post_meta( $id, '_wp_attachment_image_alt', true ),
74        );
75    }
76
77    /**
78     * Preserve an existing custom fallback, otherwise use the configured image.
79     *
80     * @param array $image Existing custom fallback.
81     * @return array
82     */
83    public static function fallback_image( $image ) {
84        if ( Seo_Tools::is_disabled() ) {
85            return $image;
86        }
87        return ! empty( $image['src'] ) ? $image : ( self::image( (int) get_option( self::OPTION, 0 ) ) ?? $image );
88    }
89}