Code Coverage
 
Lines
Functions and Methods
Classes and Traits
Total
0.00% covered (danger)
0.00%
0 / 315
0.00% covered (danger)
0.00%
0 / 55
CRAP
0.00% covered (danger)
0.00%
0 / 17
Sharing_Source_Block
0.00% covered (danger)
0.00%
0 / 86
0.00% covered (danger)
0.00%
0 / 16
506
0.00% covered (danger)
0.00%
0 / 1
 __construct
0.00% covered (danger)
0.00%
0 / 1
0.00% covered (danger)
0.00%
0 / 1
2
 http
0.00% covered (danger)
0.00%
0 / 1
0.00% covered (danger)
0.00%
0 / 1
2
 get_id
0.00% covered (danger)
0.00%
0 / 1
0.00% covered (danger)
0.00%
0 / 1
2
 get_class
0.00% covered (danger)
0.00%
0 / 1
0.00% covered (danger)
0.00%
0 / 1
2
 get_total
0.00% covered (danger)
0.00%
0 / 18
0.00% covered (danger)
0.00%
0 / 1
6
 get_share_url
0.00% covered (danger)
0.00%
0 / 1
0.00% covered (danger)
0.00%
0 / 1
2
 get_share_title
0.00% covered (danger)
0.00%
0 / 3
0.00% covered (danger)
0.00%
0 / 1
6
 get_share_tags
0.00% covered (danger)
0.00%
0 / 12
0.00% covered (danger)
0.00%
0 / 1
6
 get_link
0.00% covered (danger)
0.00%
0 / 6
0.00% covered (danger)
0.00%
0 / 1
2
 get_url
0.00% covered (danger)
0.00%
0 / 10
0.00% covered (danger)
0.00%
0 / 1
12
 js_dialog
0.00% covered (danger)
0.00%
0 / 1
0.00% covered (danger)
0.00%
0 / 1
2
 get_data_attributes
0.00% covered (danger)
0.00%
0 / 19
0.00% covered (danger)
0.00%
0 / 1
6
 get_process_request_url
0.00% covered (danger)
0.00%
0 / 1
0.00% covered (danger)
0.00%
0 / 1
2
 has_advanced_options
0.00% covered (danger)
0.00%
0 / 1
0.00% covered (danger)
0.00%
0 / 1
2
 process_request
0.00% covered (danger)
0.00%
0 / 7
0.00% covered (danger)
0.00%
0 / 1
2
 redirect_request
0.00% covered (danger)
0.00%
0 / 3
0.00% covered (danger)
0.00%
0 / 1
2
Share_Email_Block
0.00% covered (danger)
0.00%
0 / 49
0.00% covered (danger)
0.00%
0 / 4
156
0.00% covered (danger)
0.00%
0 / 1
 get_name
0.00% covered (danger)
0.00%
0 / 1
0.00% covered (danger)
0.00%
0 / 1
2
 get_email_share_nonce_action
0.00% covered (danger)
0.00%
0 / 3
0.00% covered (danger)
0.00%
0 / 1
6
 get_link
0.00% covered (danger)
0.00%
0 / 32
0.00% covered (danger)
0.00%
0 / 1
6
 process_request
0.00% covered (danger)
0.00%
0 / 13
0.00% covered (danger)
0.00%
0 / 1
56
Share_Facebook_Block
0.00% covered (danger)
0.00%
0 / 5
0.00% covered (danger)
0.00%
0 / 2
12
0.00% covered (danger)
0.00%
0 / 1
 get_name
0.00% covered (danger)
0.00%
0 / 1
0.00% covered (danger)
0.00%
0 / 1
2
 process_request
0.00% covered (danger)
0.00%
0 / 4
0.00% covered (danger)
0.00%
0 / 1
6
Share_Print_Block
0.00% covered (danger)
0.00%
0 / 1
0.00% covered (danger)
0.00%
0 / 1
2
0.00% covered (danger)
0.00%
0 / 1
 get_name
0.00% covered (danger)
0.00%
0 / 1
0.00% covered (danger)
0.00%
0 / 1
2
Share_Native_Block
0.00% covered (danger)
0.00%
0 / 1
0.00% covered (danger)
0.00%
0 / 1
2
0.00% covered (danger)
0.00%
0 / 1
 get_name
0.00% covered (danger)
0.00%
0 / 1
0.00% covered (danger)
0.00%
0 / 1
2
Share_Tumblr_Block
0.00% covered (danger)
0.00%
0 / 4
0.00% covered (danger)
0.00%
0 / 2
6
0.00% covered (danger)
0.00%
0 / 1
 get_name
0.00% covered (danger)
0.00%
0 / 1
0.00% covered (danger)
0.00%
0 / 1
2
 process_request
0.00% covered (danger)
0.00%
0 / 3
0.00% covered (danger)
0.00%
0 / 1
2
Share_Pinterest_Block
0.00% covered (danger)
0.00%
0 / 14
0.00% covered (danger)
0.00%
0 / 5
56
0.00% covered (danger)
0.00%
0 / 1
 get_name
0.00% covered (danger)
0.00%
0 / 1
0.00% covered (danger)
0.00%
0 / 1
2
 get_image
0.00% covered (danger)
0.00%
0 / 4
0.00% covered (danger)
0.00%
0 / 1
6
 get_external_url
0.00% covered (danger)
0.00%
0 / 2
0.00% covered (danger)
0.00%
0 / 1
2
 get_widget_type
0.00% covered (danger)
0.00%
0 / 1
0.00% covered (danger)
0.00%
0 / 1
2
 process_request
0.00% covered (danger)
0.00%
0 / 6
0.00% covered (danger)
0.00%
0 / 1
6
Share_Telegram_Block
0.00% covered (danger)
0.00%
0 / 4
0.00% covered (danger)
0.00%
0 / 2
6
0.00% covered (danger)
0.00%
0 / 1
 get_name
0.00% covered (danger)
0.00%
0 / 1
0.00% covered (danger)
0.00%
0 / 1
2
 process_request
0.00% covered (danger)
0.00%
0 / 3
0.00% covered (danger)
0.00%
0 / 1
2
Jetpack_Share_WhatsApp_Block
0.00% covered (danger)
0.00%
0 / 7
0.00% covered (danger)
0.00%
0 / 2
12
0.00% covered (danger)
0.00%
0 / 1
 get_name
0.00% covered (danger)
0.00%
0 / 1
0.00% covered (danger)
0.00%
0 / 1
2
 process_request
0.00% covered (danger)
0.00%
0 / 6
0.00% covered (danger)
0.00%
0 / 1
6
Share_Mastodon_Block
0.00% covered (danger)
0.00%
0 / 25
0.00% covered (danger)
0.00%
0 / 2
20
0.00% covered (danger)
0.00%
0 / 1
 get_name
0.00% covered (danger)
0.00%
0 / 1
0.00% covered (danger)
0.00%
0 / 1
2
 process_request
0.00% covered (danger)
0.00%
0 / 24
0.00% covered (danger)
0.00%
0 / 1
12
Share_Nextdoor_Block
0.00% covered (danger)
0.00%
0 / 5
0.00% covered (danger)
0.00%
0 / 2
6
0.00% covered (danger)
0.00%
0 / 1
 get_name
0.00% covered (danger)
0.00%
0 / 1
0.00% covered (danger)
0.00%
0 / 1
2
 process_request
0.00% covered (danger)
0.00%
0 / 4
0.00% covered (danger)
0.00%
0 / 1
2
Share_Bluesky_Block
0.00% covered (danger)
0.00%
0 / 5
0.00% covered (danger)
0.00%
0 / 2
6
0.00% covered (danger)
0.00%
0 / 1
 get_name
0.00% covered (danger)
0.00%
0 / 1
0.00% covered (danger)
0.00%
0 / 1
2
 process_request
0.00% covered (danger)
0.00%
0 / 4
0.00% covered (danger)
0.00%
0 / 1
2
Share_X_Block
0.00% covered (danger)
0.00%
0 / 45
0.00% covered (danger)
0.00%
0 / 4
132
0.00% covered (danger)
0.00%
0 / 1
 get_name
0.00% covered (danger)
0.00%
0 / 1
0.00% covered (danger)
0.00%
0 / 1
2
 sharing_x_via
0.00% covered (danger)
0.00%
0 / 10
0.00% covered (danger)
0.00%
0 / 1
6
 get_related_accounts
0.00% covered (danger)
0.00%
0 / 8
0.00% covered (danger)
0.00%
0 / 1
12
 process_request
0.00% covered (danger)
0.00%
0 / 26
0.00% covered (danger)
0.00%
0 / 1
30
Share_Twitter_Block
0.00% covered (danger)
0.00%
0 / 45
0.00% covered (danger)
0.00%
0 / 4
132
0.00% covered (danger)
0.00%
0 / 1
 get_name
0.00% covered (danger)
0.00%
0 / 1
0.00% covered (danger)
0.00%
0 / 1
2
 sharing_twitter_via
0.00% covered (danger)
0.00%
0 / 10
0.00% covered (danger)
0.00%
0 / 1
6
 get_related_accounts
0.00% covered (danger)
0.00%
0 / 8
0.00% covered (danger)
0.00%
0 / 1
12
 process_request
0.00% covered (danger)
0.00%
0 / 26
0.00% covered (danger)
0.00%
0 / 1
30
Share_Reddit_Block
0.00% covered (danger)
0.00%
0 / 4
0.00% covered (danger)
0.00%
0 / 2
6
0.00% covered (danger)
0.00%
0 / 1
 get_name
0.00% covered (danger)
0.00%
0 / 1
0.00% covered (danger)
0.00%
0 / 1
2
 process_request
0.00% covered (danger)
0.00%
0 / 3
0.00% covered (danger)
0.00%
0 / 1
2
Share_LinkedIn_Block
0.00% covered (danger)
0.00%
0 / 10
0.00% covered (danger)
0.00%
0 / 2
6
0.00% covered (danger)
0.00%
0 / 1
 get_name
0.00% covered (danger)
0.00%
0 / 1
0.00% covered (danger)
0.00%
0 / 1
2
 process_request
0.00% covered (danger)
0.00%
0 / 9
0.00% covered (danger)
0.00%
0 / 1
2
Share_Threads_Block
0.00% covered (danger)
0.00%
0 / 5
0.00% covered (danger)
0.00%
0 / 2
6
0.00% covered (danger)
0.00%
0 / 1
 get_name
0.00% covered (danger)
0.00%
0 / 1
0.00% covered (danger)
0.00%
0 / 1
2
 process_request
0.00% covered (danger)
0.00%
0 / 4
0.00% covered (danger)
0.00%
0 / 1
2
1<?php
2/**
3 * Define all sharing sources.
4 *
5 * @since 13.0
6 *
7 * @package automattic/jetpack
8 *
9 * phpcs:disable Generic.Files.OneObjectStructurePerFile.MultipleFound
10 */
11
12namespace Automattic\Jetpack\Extensions\Sharing_Button_Block;
13
14use Automattic\Jetpack\Device_Detection\User_Agent_Info;
15use Automattic\Jetpack\Post_Media\Images;
16use WP_Post;
17
18/**
19 * Base class for sharing sources.
20 * See individual sharing classes below for the implementation of this class.
21 */
22abstract class Sharing_Source_Block {
23    /**
24     * Sharing unique ID.
25     *
26     * @var int
27     */
28    protected $id;
29
30    /**
31     * Constructor.
32     *
33     * @param int $id       Sharing source ID.
34     */
35    public function __construct( $id ) {
36        $this->id = $id;
37    }
38
39    /**
40     * Get the protocol to use for a sharing service, based on the site settings.
41     *
42     * @return string
43     */
44    public function http() {
45        return 'https';
46    }
47
48    /**
49     * Get unique sharing ID.
50     *
51     * @return int
52     */
53    public function get_id() {
54        return $this->id;
55    }
56
57    /**
58     * Get unique sharing ID. Similar to get_id().
59     *
60     * @return int
61     */
62    public function get_class() {
63        return $this->id;
64    }
65
66    /**
67     * Get stats for a site, a post, or a sharing service.
68     * Soon to come to a .org plugin near you!
69     *
70     * @param WP_Post|bool $post Post object.
71     *
72     * @return int
73     */
74    public function get_total( $post = false ) {
75        global $wpdb, $blog_id;
76
77        $name = strtolower( (string) $this->get_id() );
78
79        if ( $post === false ) {
80            // get total number of shares for service
81            return (int) $wpdb->get_var( // phpcs:ignore WordPress.DB.DirectDatabaseQuery
82                $wpdb->prepare(
83                    'SELECT SUM( count ) FROM sharing_stats WHERE blog_id = %d AND share_service = %s',
84                    $blog_id,
85                    $name
86                )
87            );
88        }
89
90        // get total shares for a post
91        return (int) $wpdb->get_var( // phpcs:ignore WordPress.DB.DirectDatabaseQuery
92            $wpdb->prepare(
93                'SELECT count FROM sharing_stats WHERE blog_id = %d AND post_id = %d AND share_service = %s',
94                $blog_id,
95                $post->ID,
96                $name
97            )
98        );
99    }
100
101    /**
102     * Get a post's permalink to use for sharing.
103     *
104     * @param int $post_id Post ID.
105     *
106     * @return string
107     */
108    public function get_share_url( $post_id ) {
109        /**
110         * Filter the sharing permalink.
111         *
112         * @module sharedaddy
113         *
114         * @since 1.2.0
115         *
116         * @param string get_permalink( $post_id ) Post Permalink.
117         * @param int $post_id Post ID.
118         * @param int $this->id Sharing ID.
119         */
120        return apply_filters( 'sharing_permalink', get_permalink( $post_id ), $post_id, $this->id );
121    }
122
123    /**
124     * Get a post's title to use for sharing.
125     *
126     * @param int $post_id Post ID.
127     *
128     * @return string
129     */
130    public function get_share_title( $post_id ) {
131        $post = get_post( $post_id );
132        /**
133         * Filter the sharing title.
134         *
135         * @module sharedaddy
136         *
137         * @since 2.8.0
138         *
139         * @param string $post->post_title Post Title.
140         * @param int $post_id Post ID.
141         * @param int $this->id Sharing ID.
142         */
143        $title = $post instanceof WP_Post ? apply_filters( 'sharing_title', $post->post_title, $post_id, $this->id ) : '';
144        return html_entity_decode( wp_kses( $title, '' ), ENT_QUOTES | ENT_SUBSTITUTE | ENT_HTML401 );
145    }
146
147    /**
148     * Get a comma-separated list of the post's tags to use for sharing.
149     * Prepends a '#' to each tag.
150     *
151     * @param int $post_id Post ID.
152     *
153     * @return string
154     */
155    public function get_share_tags( $post_id ) {
156        $tags = get_the_tags( $post_id );
157        if ( ! $tags ) {
158            return '';
159        }
160
161        $tags = array_map(
162            function ( $tag ) {
163                // Camel case the tag name and remove spaces as well as apostrophes.
164                $tag = preg_replace( '/\s+|\'/', '', ucwords( $tag->name ) );
165
166                // Return with a '#' prepended.
167                return '#' . $tag;
168            },
169            $tags
170        );
171
172        /**
173         * Allow customizing how the list of tags is displayed.
174         *
175         * @module sharedaddy
176         * @since 11.9
177         *
178         * @param string $tags     Comma-separated list of tags.
179         * @param int    $post_id  Post ID.
180         * @param int    $this->id Sharing ID.
181         */
182        $tag_list = (string) apply_filters( 'jetpack_sharing_tag_list', implode( ', ', $tags ), $post_id, $this->id );
183
184        return html_entity_decode( wp_kses( $tag_list, '' ), ENT_QUOTES | ENT_SUBSTITUTE | ENT_HTML401 );
185    }
186
187    /**
188     * Get the URL for the link.
189     *
190     * @param int         $post_id         Post ID.
191     * @param string      $query           Additional query arguments to add to the link. They should be in 'foo=bar&baz=1' format.
192     * @param bool|string $id              Sharing ID to include in the data-shared attribute.
193     * @param array       $data_attributes The keys are used as additional attribute names with 'data-' prefix.
194     *                                     The values are used as the attribute values.
195     * @return object Link related data (url and data_attributes);
196     */
197    public function get_link( $post_id, $query = '', $id = false, $data_attributes = array() ) {
198        $url             = $this->get_url( $this->get_process_request_url( $post_id ), $query, $id );
199        $data_attributes = $this->get_data_attributes( $id, $data_attributes );
200
201        return array(
202            'url'             => $url,
203            'data_attributes' => $data_attributes,
204        );
205    }
206
207    /**
208     * Get the URL for the link.
209     *
210     * @param string      $url             Post URL to share.
211     * @param string      $query           Additional query arguments to add to the link. They should be in 'foo=bar&baz=1' format.
212     * @param bool|string $id              Sharing ID to include in the data-shared attribute.
213     *
214     * @return string The link URL.
215     */
216    public function get_url( $url, $query = '', $id = false ) {
217        $args = func_get_args();
218
219        /**
220         * Filter the sharing display ID.
221         *
222         * @module sharedaddy
223         *
224         * @since 3.4.0
225         *
226         * @param string|false $id Sharing ID.
227         * @param object $this Sharing service properties.
228         * @param array $args Array of sharing service options.
229         */
230        $id = apply_filters( 'jetpack_sharing_display_id', $id, $this, $args );
231        /**
232         * Filter the sharing display link.
233         *
234         * @module sharedaddy
235         *
236         * @since 2.8.0
237         *
238         * @param string $url Post URL.
239         * @param object $this Sharing service properties.
240         * @param string|false $id Sharing ID.
241         * @param array $args Array of sharing service options.
242         */
243        $url = apply_filters( 'sharing_display_link', $url, $this, $id, $args ); // backwards compatibility
244        /**
245         * Filter the sharing display link.
246         *
247         * @module sharedaddy
248         *
249         * @since 2.8.0
250         *
251         * @param string $url Post URL.
252         * @param object $this Sharing service properties.
253         * @param string|false $id Sharing ID.
254         * @param array $args Array of sharing service options.
255         */
256        $url = apply_filters( 'jetpack_sharing_display_link', $url, $this, $id, $args );
257        /**
258         * Filter the sharing display query.
259         *
260         * @module sharedaddy
261         *
262         * @since 2.8.0
263         *
264         * @param string $query Sharing service URL parameter.
265         * @param object $this Sharing service properties.
266         * @param string|false $id Sharing ID.
267         * @param array $args Array of sharing service options.
268         */
269        $query = apply_filters( 'jetpack_sharing_display_query', $query, $this, $id, $args );
270
271        if ( ! empty( $query ) ) {
272            if ( false === stripos( $url, '?' ) ) {
273                $url .= '?' . $query;
274            } else {
275                $url .= '&amp;' . $query;
276            }
277        }
278
279        return $url;
280    }
281
282    /**
283     * Add extra JavaScript to a sharing service.
284     *
285     * @param array $params Array of sharing options.
286     *
287     * @return void
288     */
289    public function js_dialog( $params = array() ) {
290    }
291
292    /**
293     * Get custom data attributes for the link.
294     *
295     * @param bool|string $id              Sharing ID to include in the data-shared attribute.
296     * @param array       $data_attributes The keys are used as additional attribute names with 'data-' prefix.
297     *                                     The values are used as the attribute values.
298     *
299     * @return string Encoded data attributes.
300     */
301    public function get_data_attributes( $id = false, $data_attributes = array() ) {
302        $args = func_get_args();
303
304        /**
305         * Filter the sharing data attributes.
306         *
307         * @module sharedaddy
308         *
309         * @since 11.0
310         *
311         * @param array $data_attributes Attributes supplied from the sharing source.
312         *                               Note that 'data-' will be prepended to all keys.
313         * @param Sharing_Source $this Sharing source instance.
314         * @param string|false $id Sharing ID.
315         * @param array $args Array of sharing service options.
316         */
317        $data_attributes = apply_filters( 'jetpack_sharing_data_attributes', (array) $data_attributes, $this, $id, $args );
318
319        $encoded_data_attributes = '';
320        if ( ! empty( $data_attributes ) ) {
321            $encoded_data_attributes = implode(
322                ' ',
323                array_map(
324                    /** Format attributes */
325                    function ( $data_key, $data_value ) {
326                        return sprintf(
327                            'data-%s="%s"',
328                            esc_attr( str_replace( array( ' ', '"' ), '', $data_key ) ),
329                            esc_attr( $data_value )
330                        );
331                    },
332                    array_keys( $data_attributes ),
333                    array_values( $data_attributes )
334                )
335            );
336        }
337        return $encoded_data_attributes;
338    }
339
340    /**
341     * Get an unfiltered post permalink to use when generating a sharing URL with get_link.
342     * Use instead of get_share_url for non-official styles as get_permalink ensures that process_request
343     * will be executed more reliably, in the case that the filtered URL uses a service that strips query parameters.
344     *
345     * @since 3.7.0
346     * @param int $post_id Post ID.
347     *
348     * @uses get_permalink
349     *
350     * @return string get_permalink( $post_id ) Post permalink.
351     */
352    public function get_process_request_url( $post_id ) {
353        return get_permalink( $post_id );
354    }
355
356    /**
357     * Does the service have advanced options.
358     *
359     * @return bool
360     */
361    public function has_advanced_options() {
362        return false;
363    }
364
365    /**
366     * Process sharing request. Add actions that need to happen when sharing here.
367     *
368     * @param WP_Post $post Post object.
369     * @param array   $post_data Array of information about the post we're sharing.
370     *
371     * @return void
372     */
373    public function process_request( $post, array $post_data ) { // phpcs:ignore VariableAnalysis.CodeAnalysis.VariableAnalysis.UnusedVariable
374        /**
375         * Fires when a post is shared via one of the sharing buttons.
376         *
377         * @module sharedaddy
378         *
379         * @since 1.1.0
380         *
381         * @param array $args Aray of information about the sharing service.
382         */
383        do_action(
384            'sharing_bump_stats',
385            array(
386                'service' => $this,
387                'post'    => $post,
388            )
389        );
390    }
391
392    /**
393     * Redirect to an external social network site to finish sharing.
394     *
395     * @param string $url Sharing URL for a given service.
396     * @return never
397     */
398    public function redirect_request( $url ) {
399        wp_redirect( $url ); // phpcs:ignore WordPress.Security.SafeRedirect.wp_redirect_wp_redirect -- We allow external redirects here; we define them ourselves.
400
401        // We set up this custom header to indicate to search engines not to index this page.
402        header( 'X-Robots-Tag: noindex, nofollow' );
403        die( 0 );
404    }
405}
406
407/**
408 * Handle the display of the email sharing button.
409 */
410class Share_Email_Block extends Sharing_Source_Block {
411    /**
412     * Service short name.
413     *
414     * @var string
415     */
416    public $shortname = 'email';
417
418    /**
419     * Service name.
420     *
421     * @return string
422     */
423    public function get_name() {
424        return _x( 'Email', 'as sharing source', 'jetpack' );
425    }
426
427    /**
428     * Helper function to return a nonce action based on the current post.
429     *
430     * @param int $post_id The current post id if it is defined.
431     * @return string The nonce action name.
432     */
433    protected function get_email_share_nonce_action( $post_id ) {
434        if ( ! empty( $post_id ) ) {
435            return 'jetpack-email-share-' . $post_id;
436        }
437
438        return 'jetpack-email-share';
439    }
440
441    /**
442     * Get the URL for the link.
443     *
444     * @param int         $post_id         Post ID.
445     * @param string      $query           Additional query arguments to add to the link. They should be in 'foo=bar&baz=1' format.
446     * @param bool|string $id              Sharing ID to include in the data-shared attribute.
447     * @param array       $data_attributes The keys are used as additional attribute names with 'data-' prefix.
448     *                                     The values are used as the attribute values.
449     * @return object Link related data (url and data_attributes);
450     */
451    public function get_link( $post_id, $query = '', $id = false, $data_attributes = array() ) {
452        // We don't need to open new window, so we set it to false.
453        $id           = false;
454        $tracking_url = $this->get_process_request_url( $post_id );
455        if ( false === stripos( $tracking_url, '?' ) ) {
456            $tracking_url .= '?';
457        } else {
458            $tracking_url .= '&';
459        }
460        $tracking_url .= 'share=email';
461
462        $data_attributes = array(
463            'email-share-error-title' => __( 'Do you have email set up?', 'jetpack' ),
464            'email-share-error-text'  => __(
465                "If you're having problems sharing via email, you might not have email set up for your browser. You may need to create a new email yourself.",
466                'jetpack'
467            ),
468            'email-share-nonce'       => wp_create_nonce( $this->get_email_share_nonce_action( $post_id ) ),
469            'email-share-track-url'   => $tracking_url,
470        );
471
472        $post_title = $this->get_share_title( $post_id );
473        $post_url   = $this->get_share_url( $post_id );
474
475        /** This filter is documented in plugins/jetpack/modules/sharedaddy/sharedaddy.php */
476        $email_subject = apply_filters(
477            'wp_sharing_email_send_post_subject',
478            sprintf( '[%s] %s', __( 'Shared Post', 'jetpack' ), $post_title )
479        );
480
481        $mailto_query = sprintf(
482            'subject=%s&body=%s&share=email',
483            rawurlencode( $email_subject ),
484            rawurlencode( $post_url )
485        );
486
487        $url             = $this->get_url( 'mailto:', $mailto_query, $id );
488        $data_attributes = $this->get_data_attributes( $id, $data_attributes );
489
490        return array(
491            'url'             => $url,
492            'data_attributes' => $data_attributes,
493        );
494    }
495
496    /**
497     * Process sharing request. Add actions that need to happen when sharing here.
498     *
499     * @param WP_Post $post Post object.
500     * @param array   $post_data Array of information about the post we're sharing.
501     *
502     * @return void
503     */
504    public function process_request( $post, array $post_data ) {
505        $is_ajax = false;
506        if (
507            isset( $_SERVER['HTTP_X_REQUESTED_WITH'] )
508            && strtolower( sanitize_text_field( wp_unslash( $_SERVER['HTTP_X_REQUESTED_WITH'] ) ) ) === 'xmlhttprequest'
509        ) {
510            $is_ajax = true;
511        }
512
513        // Require an AJAX-driven submit and a valid nonce to process the request
514        if (
515            $is_ajax
516            && isset( $post_data['email-share-nonce'] )
517            && wp_verify_nonce( $post_data['email-share-nonce'], $this->get_email_share_nonce_action( $post ) )
518        ) {
519            // Ensure that we bump stats
520            parent::process_request( $post, $post_data );
521        }
522
523        if ( $is_ajax ) {
524            // @phan-suppress-next-line PhanTypeMismatchArgumentProbablyReal -- It takes null, but its phpdoc only says int.
525            wp_send_json_success( null, null, JSON_UNESCAPED_SLASHES );
526        } else {
527            wp_safe_redirect( get_permalink( $post->ID ) . '?shared=email&msg=fail' );
528            exit( 0 );
529        }
530
531        wp_die();
532    }
533}
534
535/**
536 * Facebook sharing button.
537 */
538class Share_Facebook_Block extends Sharing_Source_Block {
539    /**
540     * Service short name.
541     *
542     * @var string
543     */
544    public $shortname = 'facebook';
545
546    /**
547     * Sharing type.
548     *
549     * @var string
550     */
551    private $share_type = 'default';
552
553    /**
554     * Service name.
555     *
556     * @return string
557     */
558    public function get_name() {
559        return __( 'Facebook', 'jetpack' );
560    }
561
562    /**
563     * Process sharing request. Add actions that need to happen when sharing here.
564     *
565     * @param WP_Post $post Post object.
566     * @param array   $post_data Array of information about the post we're sharing.
567     *
568     * @return void
569     */
570    public function process_request( $post, array $post_data ) {
571        $post_id = $post instanceof WP_Post ? $post->ID : 0;
572        $fb_url  = 'https://www.facebook.com/sharer/sharer.php?u=' . rawurlencode( $this->get_share_url( $post_id ) ) . '&t=' . rawurlencode( $this->get_share_title( $post_id ) );
573
574        // Record stats
575        parent::process_request( $post, $post_data );
576
577        parent::redirect_request( $fb_url );
578    }
579}
580
581/**
582 * Print button.
583 */
584class Share_Print_Block extends Sharing_Source_Block {
585    /**
586     * Service short name.
587     *
588     * @var string
589     */
590    public $shortname = 'print';
591
592    /**
593     * Service name.
594     *
595     * @return string
596     */
597    public function get_name() {
598        return __( 'Print', 'jetpack' );
599    }
600}
601
602/**
603 * Native Share button (relying on the Web Share API).
604 */
605class Share_Native_Block extends Sharing_Source_Block {
606    /**
607     * Service short name.
608     *
609     * @var string
610     */
611    public $shortname = 'native';
612
613    /**
614     * Service name.
615     *
616     * @return string
617     */
618    public function get_name() {
619        return __( 'Web Share', 'jetpack' );
620    }
621}
622
623/**
624 * Tumblr sharing service.
625 */
626class Share_Tumblr_Block extends Sharing_Source_Block {
627    /**
628     * Service short name.
629     *
630     * @var string
631     */
632    public $shortname = 'tumblr';
633
634    /**
635     * Service name.
636     *
637     * @return string
638     */
639    public function get_name() {
640        return __( 'Tumblr', 'jetpack' );
641    }
642
643    /**
644     * Process sharing request. Add actions that need to happen when sharing here.
645     *
646     * @param WP_Post $post Post object.
647     * @param array   $post_data Array of information about the post we're sharing.
648     *
649     * @return void
650     */
651    public function process_request( $post, array $post_data ) {
652        // Record stats
653        parent::process_request( $post, $post_data );
654
655        // Redirect to Tumblr's sharing endpoint (a la their bookmarklet)
656        $url = 'https://www.tumblr.com/share?v=3&u=' . rawurlencode( $this->get_share_url( $post->ID ) ) . '&t=' . rawurlencode( $this->get_share_title( $post->ID ) ) . '&s=';
657
658        parent::redirect_request( $url );
659    }
660}
661
662/**
663 * Pinterest sharing service.
664 */
665class Share_Pinterest_Block extends Sharing_Source_Block {
666    /**
667     * Service short name.
668     *
669     * @var string
670     */
671    public $shortname = 'pinterest';
672
673    /**
674     * Service name.
675     *
676     * @return string
677     */
678    public function get_name() {
679        return __( 'Pinterest', 'jetpack' );
680    }
681
682    /**
683     * Get image representative of the post to pass on to Pinterest.
684     *
685     * @param WP_Post $post Post object.
686     *
687     * @return string
688     */
689    public function get_image( $post ) {
690        $image = Images::get_image( $post->ID, array( 'fallback_to_avatars' => true ) );
691        if ( ! empty( $image ) ) {
692            return $image['src'];
693        }
694
695        /**
696         * Filters the default image used by the Pinterest Pin It share button.
697         *
698         * @module sharedaddy
699         *
700         * @since 3.6.0
701         *
702         * @param string $url Default image URL.
703         */
704        return apply_filters( 'jetpack_sharing_pinterest_default_image', 'https://s0.wp.com/i/blank.jpg' );
705    }
706
707    /**
708     * Get Pinterest external sharing URL.
709     *
710     * @param WP_Post $post Post object.
711     *
712     * @return string
713     */
714    public function get_external_url( $post ) {
715        $url = 'https://www.pinterest.com/pin/create/button/?url=' . rawurlencode( $this->get_share_url( $post->ID ) ) . '&media=' . rawurlencode( $this->get_image( $post ) ) . '&description=' . rawurlencode( $post->post_title );
716
717        /**
718         * Filters the Pinterest share URL used in sharing button output.
719         *
720         * @module sharedaddy
721         *
722         * @since 3.6.0
723         *
724         * @param string $url Pinterest share URL.
725         */
726        return apply_filters( 'jetpack_sharing_pinterest_share_url', $url );
727    }
728
729    /**
730     * Get Pinterest widget type.
731     *
732     * @return string
733     */
734    public function get_widget_type() {
735        /**
736         * Filters the Pinterest widget type.
737         *
738         * @see https://business.pinterest.com/en/widget-builder
739         *
740         * @module sharedaddy
741         *
742         * @since 3.6.0
743         *
744         * @param string $type Pinterest widget type. Default of 'buttonPin' for single-image selection. 'buttonBookmark' for multi-image modal.
745         */
746        return apply_filters( 'jetpack_sharing_pinterest_widget_type', 'buttonPin' );
747    }
748
749    /**
750     * Process sharing request. Add actions that need to happen when sharing here.
751     *
752     * @param WP_Post $post Post object.
753     * @param array   $post_data Array of information about the post we're sharing.
754     *
755     * @return void
756     */
757    public function process_request( $post, array $post_data ) {
758        // Record stats
759        parent::process_request( $post, $post_data );
760        // If we're triggering the multi-select panel, then we don't need to redirect to Pinterest
761        if ( ! isset( $_GET['js_only'] ) ) { // phpcs:ignore WordPress.Security.NonceVerification.Recommended
762            $pinterest_url = esc_url_raw( $this->get_external_url( $post ) );
763            parent::redirect_request( $pinterest_url );
764        } else {
765            echo '// share count bumped';
766            die( 0 );
767        }
768    }
769}
770
771/**
772 * Telegram sharing service.
773 */
774class Share_Telegram_Block extends Sharing_Source_Block {
775    /**
776     * Service short name.
777     *
778     * @var string
779     */
780    public $shortname = 'telegram';
781
782    /**
783     * Service name.
784     *
785     * @return string
786     */
787    public function get_name() {
788        return __( 'Telegram', 'jetpack' );
789    }
790
791    /**
792     * Process sharing request. Add actions that need to happen when sharing here.
793     *
794     * @param WP_Post $post Post object.
795     * @param array   $post_data Array of information about the post we're sharing.
796     *
797     * @return void
798     */
799    public function process_request( $post, array $post_data ) {
800        // Record stats
801        parent::process_request( $post, $post_data );
802
803        $telegram_url = esc_url_raw( 'https://telegram.me/share/url?url=' . rawurlencode( $this->get_share_url( $post->ID ) ) . '&text=' . rawurlencode( $this->get_share_title( $post->ID ) ) );
804
805        parent::redirect_request( $telegram_url );
806    }
807}
808
809/**
810 * WhatsApp sharing service.
811 */
812class Jetpack_Share_WhatsApp_Block extends Sharing_Source_Block {
813    /**
814     * Service short name.
815     *
816     * @var string
817     */
818    public $shortname = 'jetpack-whatsapp';
819
820    /**
821     * Service name.
822     *
823     * @return string
824     */
825    public function get_name() {
826        return __( 'WhatsApp', 'jetpack' );
827    }
828
829    /**
830     * Process sharing request. Add actions that need to happen when sharing here.
831     *
832     * @param WP_Post $post Post object.
833     * @param array   $post_data Array of information about the post we're sharing.
834     *
835     * @return void
836     */
837    public function process_request( $post, array $post_data ) {
838        // Record stats
839        parent::process_request( $post, $post_data );
840
841        // Firefox for desktop doesn't handle the "api.whatsapp.com" URL properly, so use "web.whatsapp.com"
842        if ( User_Agent_Info::is_firefox_desktop() ) {
843            $url = 'https://web.whatsapp.com/send?text=';
844        } else {
845            $url = 'https://api.whatsapp.com/send?text=';
846        }
847
848        $url .= rawurlencode( $this->get_share_title( $post->ID ) . ' ' . $this->get_share_url( $post->ID ) );
849
850        parent::redirect_request( $url );
851    }
852}
853
854/**
855 * Mastodon sharing service.
856 */
857class Share_Mastodon_Block extends Sharing_Source_Block {
858    /**
859     * Service short name.
860     *
861     * @var string
862     */
863    public $shortname = 'mastodon';
864
865    /**
866     * Service name.
867     *
868     * @return string
869     */
870    public function get_name() {
871        return __( 'Mastodon', 'jetpack' );
872    }
873
874    /**
875     * Process sharing request. Add actions that need to happen when sharing here.
876     *
877     * @param WP_Post $post Post object.
878     * @param array   $post_data Array of information about the post we're sharing.
879     *
880     * @return void
881     */
882    public function process_request( $post, array $post_data ) {
883        if ( empty( $_POST['jetpack-mastodon-instance'] ) ) {
884            require_once __DIR__ . '/components/class-jetpack-mastodon-modal.php';
885            add_action( 'template_redirect', array( Jetpack_Mastodon_Modal::class, 'modal' ) );
886            return;
887        }
888
889        check_admin_referer( 'jetpack_share_mastodon_instance' );
890
891        $mastodon_instance = isset( $_POST['jetpack-mastodon-instance'] )
892            ? trailingslashit( sanitize_text_field( wp_unslash( $_POST['jetpack-mastodon-instance'] ) ) )
893            : null;
894
895        $post_title = $this->get_share_title( $post->ID );
896        $post_link  = $this->get_share_url( $post->ID );
897        $post_tags  = $this->get_share_tags( $post->ID );
898
899        /**
900         * Allow filtering the default message that gets posted to Mastodon.
901         *
902         * @module sharedaddy
903         * @since 11.9
904         *
905         * @param string  $share_url The default message that gets posted to Mastodon.
906         * @param WP_Post $post      The post object.
907         * @param array   $post_data Array of information about the post we're sharing.
908         */
909        $shared_message = apply_filters(
910            'jetpack_sharing_mastodon_default_message',
911            $post_title . ' ' . $post_link . ' ' . $post_tags,
912            $post,
913            $post_data
914        );
915
916        $share_url = sprintf(
917            '%1$sshare?text=%2$s',
918            $mastodon_instance,
919            rawurlencode( $shared_message )
920        );
921
922            // Record stats
923        parent::process_request( $post, $post_data );
924
925        parent::redirect_request( $share_url );
926    }
927}
928
929/**
930 * Nextdoor sharing service.
931 */
932class Share_Nextdoor_Block extends Sharing_Source_Block {
933    /**
934     * Service short name.
935     *
936     * @var string
937     */
938    public $shortname = 'nextdoor';
939
940    /**
941     * Service name.
942     *
943     * @return string
944     */
945    public function get_name() {
946        return __( 'Nextdoor', 'jetpack' );
947    }
948
949    /**
950     * Process sharing request. Add actions that need to happen when sharing here.
951     *
952     * @param WP_Post $post Post object.
953     * @param array   $post_data Array of information about the post we're sharing.
954     *
955     * @return void
956     */
957    public function process_request( $post, array $post_data ) {
958        // Record stats
959        parent::process_request( $post, $post_data );
960
961        $url  = 'https://nextdoor.com/sharekit/?source=jetpack&body=';
962        $url .= rawurlencode( $this->get_share_title( $post->ID ) . ' ' . $this->get_share_url( $post->ID ) );
963
964        parent::redirect_request( $url );
965    }
966}
967
968/**
969 * Bluesky sharing button.
970 */
971class Share_Bluesky_Block extends Sharing_Source_Block {
972    /**
973     * Service short name.
974     *
975     * @var string
976     */
977    public $shortname = 'bluesky';
978
979    /**
980     * Service name.
981     *
982     * @return string
983     */
984    public function get_name() {
985        return __( 'Bluesky', 'jetpack' );
986    }
987
988    /**
989     * Process sharing request. Add actions that need to happen when sharing here.
990     *
991     * @param WP_Post $post Post object.
992     * @param array   $post_data Array of information about the post we're sharing.
993     *
994     * @return void
995     */
996    public function process_request( $post, array $post_data ) {
997        // Record stats
998        parent::process_request( $post, $post_data );
999
1000        $url  = 'https://bsky.app/intent/compose?text=';
1001        $url .= rawurlencode( $this->get_share_title( $post->ID ) . ' ' . $this->get_share_url( $post->ID ) );
1002
1003        parent::redirect_request( $url );
1004    }
1005}
1006
1007/**
1008 * X sharing button.
1009 *
1010 * While the old Twitter button had an official button,
1011 * this new X button does not, since there is no official X button yet.
1012 */
1013class Share_X_Block extends Sharing_Source_Block {
1014    /**
1015     * Service short name.
1016     *
1017     * @var string
1018     */
1019    public $shortname = 'x';
1020
1021    /**
1022     * Length of a URL on X.
1023     * https://developer.twitter.com/en/docs/tco
1024     *
1025     * @var int
1026     */
1027    public $short_url_length = 24;
1028
1029    /**
1030     * Service name.
1031     *
1032     * @return string
1033     */
1034    public function get_name() {
1035        return __( 'X', 'jetpack' );
1036    }
1037
1038    /**
1039     * Determine the X 'via' value for a post.
1040     *
1041     * @param  WP_Post|int $post Post object or post ID.
1042     * @return string X handle without the preceding @.
1043     **/
1044    public static function sharing_x_via( $post ) {
1045        $post = get_post( $post );
1046        /** This filter is documented in modules/sharedaddy/sharing-sources.php */
1047        $twitter_site_tag_value = apply_filters(
1048            'jetpack_twitter_cards_site_tag',
1049            '',
1050            /** This action is documented in modules/sharedaddy/sharing-sources.php */
1051            array( 'twitter:creator' => apply_filters( 'jetpack_sharing_twitter_via', '', $post->ID ) )
1052        );
1053
1054        /*
1055         * Hack to remove the unwanted behavior of adding 'via @jetpack' which
1056         * was introduced with the adding of the Twitter cards.
1057         * This should be a temporary solution until a better method is setup.
1058         */
1059        if ( 'jetpack' === $twitter_site_tag_value ) {
1060            $twitter_site_tag_value = '';
1061        }
1062
1063        /** This filter is documented in modules/sharedaddy/sharing-sources.php */
1064        $twitter_site_tag_value = apply_filters( 'jetpack_sharing_twitter_via', $twitter_site_tag_value, $post->ID );
1065
1066        // Strip out anything other than a letter, number, or underscore.
1067        // This will prevent the inadvertent inclusion of an extra @, as well as normalizing the handle.
1068        return preg_replace( '/[^\da-z_]+/i', '', $twitter_site_tag_value );
1069    }
1070
1071    /**
1072     * Determine the 'related' X accounts for a post.
1073     *
1074     * @param  WP_Post|int $post Post object or post ID.
1075     * @return string Comma-separated list of X handles.
1076     **/
1077    public static function get_related_accounts( $post ) {
1078        $post = get_post( $post );
1079        /** This filter is documented in modules/sharedaddy/sharing-sources.php */
1080        $related_accounts = apply_filters( 'jetpack_sharing_twitter_related', array(), $post->ID );
1081
1082        // Example related string: account1,account2:Account 2 description,account3
1083        $related = array();
1084
1085        foreach ( $related_accounts as $related_account_username => $related_account_description ) {
1086            // Join the description onto the end of the username
1087            if ( $related_account_description ) {
1088                $related_account_username .= ':' . $related_account_description;
1089            }
1090
1091            $related[] = $related_account_username;
1092        }
1093
1094        return implode( ',', $related );
1095    }
1096
1097    /**
1098     * Process sharing request. Add actions that need to happen when sharing here.
1099     *
1100     * @param WP_Post $post Post object.
1101     * @param array   $post_data Array of information about the post we're sharing.
1102     *
1103     * @return void
1104     */
1105    public function process_request( $post, array $post_data ) {
1106        $post_title = $this->get_share_title( $post->ID );
1107        $post_link  = $this->get_share_url( $post->ID );
1108
1109        if ( function_exists( 'mb_stripos' ) ) {
1110            $strlen = 'mb_strlen';
1111            $substr = 'mb_substr';
1112        } else {
1113            $strlen = 'strlen';
1114            $substr = 'substr';
1115        }
1116
1117        $via     = static::sharing_x_via( $post );
1118        $related = static::get_related_accounts( $post );
1119        if ( $via ) {
1120            $sig = " via @$via";
1121            if ( $related === $via ) {
1122                $related = false;
1123            }
1124        } else {
1125            $via = false;
1126            $sig = '';
1127        }
1128
1129        $suffix_length = $this->short_url_length + $strlen( $sig );
1130        // $sig is handled by twitter in their 'via' argument.
1131        // $post_link is handled by twitter in their 'url' argument.
1132        if ( 280 < $strlen( $post_title ) + $suffix_length ) {
1133            // The -1 is for "\xE2\x80\xA6", a UTF-8 ellipsis.
1134            $text = $substr( $post_title, 0, 280 - $suffix_length - 1 ) . "\xE2\x80\xA6";
1135        } else {
1136            $text = $post_title;
1137        }
1138
1139        // Record stats
1140        parent::process_request( $post, $post_data );
1141
1142        $url         = $post_link;
1143        $twitter_url = add_query_arg(
1144            rawurlencode_deep( array_filter( compact( 'via', 'related', 'text', 'url' ) ) ),
1145            'https://x.com/intent/tweet'
1146        );
1147
1148        parent::redirect_request( $twitter_url );
1149    }
1150}
1151
1152/**
1153 * Twitter sharing button.
1154 */
1155class Share_Twitter_Block extends Sharing_Source_Block {
1156    /**
1157     * Service short name.
1158     *
1159     * @var string
1160     */
1161    public $shortname = 'twitter';
1162
1163    /**
1164     * Length of a URL on Twitter.
1165     * 'https://dev.twitter.com/rest/reference/get/help/configuration'
1166     * ( 2015/02/06 ) short_url_length is 22, short_url_length_https is 23
1167     *
1168     * @var int
1169     */
1170    public $short_url_length = 24;
1171
1172    /**
1173     * Service name.
1174     *
1175     * @return string
1176     */
1177    public function get_name() {
1178        return __( 'X', 'jetpack' );
1179    }
1180
1181    /**
1182     * Determine the Twitter 'via' value for a post.
1183     *
1184     * @param  WP_Post|int $post Post object or post ID.
1185     * @return string Twitter handle without the preceding @.
1186     **/
1187    public static function sharing_twitter_via( $post ) {
1188        $post = get_post( $post );
1189        /**
1190         * Allow third-party plugins to customize the Twitter username used as "twitter:site" Twitter Card Meta Tag.
1191         *
1192         * @module sharedaddy
1193         *
1194         * @since 3.0.0
1195         *
1196         * @param string $string Twitter Username.
1197         * @param array $args Array of Open Graph Meta Tags and Twitter Cards tags.
1198         */
1199        $twitter_site_tag_value = apply_filters(
1200            'jetpack_twitter_cards_site_tag',
1201            '',
1202            /** This action is documented in modules/sharedaddy/sharing-sources.php */
1203            array( 'twitter:creator' => apply_filters( 'jetpack_sharing_twitter_via', '', $post->ID ) )
1204        );
1205
1206        /*
1207         * Hack to remove the unwanted behavior of adding 'via @jetpack' which
1208         * was introduced with the adding of the Twitter cards.
1209         * This should be a temporary solution until a better method is setup.
1210         */
1211        if ( 'jetpack' === $twitter_site_tag_value ) {
1212            $twitter_site_tag_value = '';
1213        }
1214
1215        /**
1216         * Filters the Twitter username used as "via" in the Twitter sharing button.
1217         *
1218         * @module sharedaddy
1219         *
1220         * @since 1.7.0
1221         *
1222         * @param string $twitter_site_tag_value Twitter Username.
1223         * @param int $post->ID Post ID.
1224         */
1225        $twitter_site_tag_value = apply_filters( 'jetpack_sharing_twitter_via', $twitter_site_tag_value, $post->ID );
1226
1227        // Strip out anything other than a letter, number, or underscore.
1228        // This will prevent the inadvertent inclusion of an extra @, as well as normalizing the handle.
1229        return preg_replace( '/[^\da-z_]+/i', '', $twitter_site_tag_value );
1230    }
1231
1232    /**
1233     * Determine the 'related' Twitter accounts for a post.
1234     *
1235     * @param  WP_Post|int $post Post object or post ID.
1236     * @return string Comma-separated list of Twitter handles.
1237     **/
1238    public static function get_related_accounts( $post ) {
1239        $post = get_post( $post );
1240        /**
1241         * Filter the list of related Twitter accounts added to the Twitter sharing button.
1242         *
1243         * @module sharedaddy
1244         *
1245         * @since 1.7.0
1246         *
1247         * @param array $args Array of Twitter usernames. Format is 'username' => 'Optional description'
1248         * @param int $post->ID Post ID.
1249         */
1250        $related_accounts = apply_filters( 'jetpack_sharing_twitter_related', array(), $post->ID );
1251
1252        // Example related string: account1,account2:Account 2 description,account3
1253        $related = array();
1254
1255        foreach ( $related_accounts as $related_account_username => $related_account_description ) {
1256            // Join the description onto the end of the username
1257            if ( $related_account_description ) {
1258                $related_account_username .= ':' . $related_account_description;
1259            }
1260
1261            $related[] = $related_account_username;
1262        }
1263
1264        return implode( ',', $related );
1265    }
1266
1267    /**
1268     * Process sharing request. Add actions that need to happen when sharing here.
1269     *
1270     * @param WP_Post $post Post object.
1271     * @param array   $post_data Array of information about the post we're sharing.
1272     *
1273     * @return void
1274     */
1275    public function process_request( $post, array $post_data ) {
1276        $post_title = $this->get_share_title( $post->ID );
1277        $post_link  = $this->get_share_url( $post->ID );
1278
1279        if ( function_exists( 'mb_stripos' ) ) {
1280            $strlen = 'mb_strlen';
1281            $substr = 'mb_substr';
1282        } else {
1283            $strlen = 'strlen';
1284            $substr = 'substr';
1285        }
1286
1287        $via     = static::sharing_twitter_via( $post );
1288        $related = static::get_related_accounts( $post );
1289        if ( $via ) {
1290            $sig = " via @$via";
1291            if ( $related === $via ) {
1292                $related = false;
1293            }
1294        } else {
1295            $via = false;
1296            $sig = '';
1297        }
1298
1299        $suffix_length = $this->short_url_length + $strlen( $sig );
1300        // $sig is handled by twitter in their 'via' argument.
1301        // $post_link is handled by twitter in their 'url' argument.
1302        if ( 280 < $strlen( $post_title ) + $suffix_length ) {
1303            // The -1 is for "\xE2\x80\xA6", a UTF-8 ellipsis.
1304            $text = $substr( $post_title, 0, 280 - $suffix_length - 1 ) . "\xE2\x80\xA6";
1305        } else {
1306            $text = $post_title;
1307        }
1308
1309        // Record stats
1310        parent::process_request( $post, $post_data );
1311
1312        $url         = $post_link;
1313        $twitter_url = add_query_arg(
1314            rawurlencode_deep( array_filter( compact( 'via', 'related', 'text', 'url' ) ) ),
1315            'https://x.com/intent/tweet'
1316        );
1317
1318        parent::redirect_request( $twitter_url );
1319    }
1320}
1321
1322/**
1323 * Reddit sharing button.
1324 */
1325class Share_Reddit_Block extends Sharing_Source_Block {
1326    /**
1327     * Service short name.
1328     *
1329     * @var string
1330     */
1331    public $shortname = 'reddit';
1332
1333    /**
1334     * Service name.
1335     *
1336     * @return string
1337     */
1338    public function get_name() {
1339        return __( 'Reddit', 'jetpack' );
1340    }
1341
1342    /**
1343     * Process sharing request. Add actions that need to happen when sharing here.
1344     *
1345     * @param WP_Post $post Post object.
1346     * @param array   $post_data Array of information about the post we're sharing.
1347     *
1348     * @return void
1349     */
1350    public function process_request( $post, array $post_data ) {
1351        $reddit_url = $this->http() . '://reddit.com/submit?url=' . rawurlencode( $this->get_share_url( $post->ID ) ) . '&title=' . rawurlencode( $this->get_share_title( $post->ID ) );
1352
1353        // Record stats
1354        parent::process_request( $post, $post_data );
1355
1356        parent::redirect_request( $reddit_url );
1357    }
1358}
1359
1360/**
1361 * LinkedIn sharing button.
1362 */
1363class Share_LinkedIn_Block extends Sharing_Source_Block {
1364    /**
1365     * Service short name.
1366     *
1367     * @var string
1368     */
1369    public $shortname = 'linkedin';
1370
1371    /**
1372     * Service name.
1373     *
1374     * @return string
1375     */
1376    public function get_name() {
1377        return __( 'LinkedIn', 'jetpack' );
1378    }
1379
1380    /**
1381     * Process sharing request. Add actions that need to happen when sharing here.
1382     *
1383     * @param WP_Post $post Post object.
1384     * @param array   $post_data Array of information about the post we're sharing.
1385     *
1386     * @return void
1387     */
1388    public function process_request( $post, array $post_data ) {
1389
1390        $post_link = $this->get_share_url( $post->ID );
1391
1392        $linkedin_url = add_query_arg(
1393            array(
1394                'url' => rawurlencode( $post_link ),
1395            ),
1396            'https://www.linkedin.com/sharing/share-offsite/'
1397        );
1398
1399        // Record stats
1400        parent::process_request( $post, $post_data );
1401
1402        parent::redirect_request( $linkedin_url );
1403    }
1404}
1405
1406/**
1407 * Threads sharing button.
1408 */
1409class Share_Threads_Block extends Sharing_Source_Block {
1410    /**
1411     * Service short name.
1412     *
1413     * @var string
1414     */
1415    public $shortname = 'threads';
1416
1417    /**
1418     * Service name.
1419     *
1420     * @return string
1421     */
1422    public function get_name() {
1423        return __( 'Threads', 'jetpack' );
1424    }
1425
1426    /**
1427     * Process sharing request. Add actions that need to happen when sharing here.
1428     *
1429     * @param WP_Post $post Post object.
1430     * @param array   $post_data Array of information about the post we're sharing.
1431     *
1432     * @return void
1433     */
1434    public function process_request( $post, array $post_data ) {
1435        // Record stats
1436        parent::process_request( $post, $post_data );
1437
1438        $url  = 'https://www.threads.net/intent/post/?text=';
1439        $url .= rawurlencode( $this->get_share_title( $post->ID ) . ' ' . $this->get_share_url( $post->ID ) );
1440
1441        parent::redirect_request( $url );
1442    }
1443}