Code Coverage
 
Lines
Functions and Methods
Classes and Traits
Total
72.88% covered (warning)
72.88%
43 / 59
25.00% covered (danger)
25.00%
1 / 4
CRAP
0.00% covered (danger)
0.00%
0 / 1
Setup
72.88% covered (warning)
72.88%
43 / 59
25.00% covered (danger)
25.00%
1 / 4
37.46
0.00% covered (danger)
0.00%
0 / 1
 init
0.00% covered (danger)
0.00%
0 / 6
0.00% covered (danger)
0.00%
0 / 1
6
 generate_token
81.82% covered (warning)
81.82%
9 / 11
0.00% covered (danger)
0.00%
0 / 1
3.05
 generate_token_on_save
70.37% covered (warning)
70.37%
19 / 27
0.00% covered (danger)
0.00%
0 / 1
24.52
 warm_social_image
100.00% covered (success)
100.00%
15 / 15
100.00% covered (success)
100.00%
1 / 1
3
1<?php
2/**
3 * Setup class.
4 *
5 * @package automattic/jetpack-publicize
6 */
7
8namespace Automattic\Jetpack\Publicize\Social_Image_Generator;
9
10use Automattic\Jetpack\Publicize\Jetpack_Social_Settings\Settings;
11
12/**
13 * Class for setting up Social Image Generator-related functionality.
14 */
15class Setup {
16    /**
17     * Initialise SIG-related functionality.
18     */
19    public function init() {
20        if ( ! ( new Settings() )->is_sig_available() ) {
21            return;
22        }
23
24        // Be wary of any code that you add to this file, since this function is called on plugin load.
25        // We're using the `wp_after_insert_post` hook because we need access to the updated post meta. By using the default priority
26        // of 10 we make sure that our code runs before Sync processes the post.
27        add_action( 'wp_after_insert_post', array( $this, 'generate_token_on_save' ), 10, 3 );
28        add_action( 'jetpack_social_sig_warm_image', array( $this, 'warm_social_image' ) );
29        add_action( 'rest_api_init', array( REST_Token_Controller::class, 'register' ) );
30
31        // Flagged to be removed after deprecation.
32        // @deprecated 0.38.3
33        add_action( 'rest_api_init', array( REST_Settings_Controller::class, 'register' ) );
34    }
35
36    /**
37     * Get a token from WPCOM to generate the social image for the post, and save it locally.
38     *
39     * @param Post_Settings $post_settings A Post_Settings object that can be used to save the generated token.
40     */
41    public function generate_token( $post_settings ) {
42        if ( ! $post_settings->is_enabled() ) {
43            return;
44        }
45
46        $token = fetch_token(
47            $post_settings->get_custom_text(),
48            $post_settings->get_image_url(),
49            $post_settings->get_template(),
50            $post_settings->get_font()
51        );
52
53        if ( is_wp_error( $token ) ) {
54            return;
55        }
56
57        $post_settings->update_setting( 'token', sanitize_text_field( $token ) );
58    }
59
60    /**
61     * Trigger token generation for a post if SIG is enabled.
62     *
63     * @param int      $post_id     Post ID.
64     * @param \WP_Post $post        The post object being saved.
65     * @param bool     $update      Whether this is an update to a post.
66     */
67    public function generate_token_on_save( $post_id, $post, $update ) {
68        if ( defined( 'WP_IMPORTING' ) && WP_IMPORTING ) {
69            return;
70        }
71
72        // If we're not using the block editor for this post, do not continue.
73        if ( ! use_block_editor_for_post( $post ) ) {
74            return;
75        }
76
77        global $publicize;
78
79        if ( ! $publicize->post_type_is_publicizeable( $post->post_type ) ) {
80            return;
81        }
82
83        $settings = new Settings();
84
85        if ( ! $settings->is_sig_available() ) {
86            return;
87        }
88
89        if ( wp_is_post_autosave( $post ) || wp_is_post_revision( $post_id ) ) {
90            return;
91        }
92
93        // Set SIG to be enabled by default for new posts if the toggle is on.
94        $post_settings = new Post_Settings( $post_id );
95        if (
96            ! $update &&
97            'auto-draft' === $post->post_status &&
98            ! empty( $settings->get_settings()['socialImageGeneratorSettings']['enabled'] ) &&
99            empty( $post_settings->get_settings( true ) ) &&
100            'jetpack-social-note' !== $post->post_type
101        ) {
102            $post_settings->update_setting( 'enabled', true );
103            return;
104        }
105
106        if ( $post->post_status === 'auto-draft' ) {
107            return;
108        }
109
110        if ( ! $post_settings->is_enabled() ) {
111            return;
112        }
113
114        $this->generate_token( $post_settings );
115
116        // Prime the Social Image Generator cache out-of-band right after publish.
117        // SIG renders the preview image on the first request to its URL, so a post
118        // shared immediately after publishing can race that cold render and end up
119        // with no preview image (notably on X, which does not retry). Warming the
120        // URL here means the image is already rendered and edge-cached before any
121        // crawler fetches it. Scheduled rather than inline so it never delays the
122        // publish request itself.
123        if (
124            'publish' === $post->post_status &&
125            ! wp_next_scheduled( 'jetpack_social_sig_warm_image', array( $post_id ) )
126        ) {
127            wp_schedule_single_event( time(), 'jetpack_social_sig_warm_image', array( $post_id ) );
128        }
129    }
130
131    /**
132     * Warm the edge cache for a post's generated social image.
133     *
134     * Runs from a scheduled single event (see generate_token_on_save) so it never
135     * blocks the publish request. Issues one blocking request to the same URL the
136     * Open Graph tags expose, which forces the on-demand render and lets the full
137     * response populate the edge cache before a crawler fetches it.
138     *
139     * @param int $post_id Post ID whose social image should be primed.
140     */
141    public function warm_social_image( $post_id ) {
142        $post_settings = new Post_Settings( $post_id );
143
144        if ( ! $post_settings->is_enabled() ) {
145            return;
146        }
147
148        $image_url = get_image_url( $post_id );
149
150        if ( empty( $image_url ) ) {
151            return;
152        }
153
154        // Blocking so the rendered response travels back through the edge cache and
155        // is stored; redirection is followed to the final image URL the crawler hits.
156        wp_remote_get(
157            $image_url,
158            array(
159                'timeout'     => 15,
160                'redirection' => 5,
161                'blocking'    => true,
162                'user-agent'  => 'WordPress.com Social Image Generator cache warmer',
163            )
164        );
165    }
166}