Code Coverage
 
Lines
Functions and Methods
Classes and Traits
Total
0.00% covered (danger)
0.00%
0 / 320
0.00% covered (danger)
0.00%
0 / 57
CRAP
0.00% covered (danger)
0.00%
0 / 18
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 / 15
0.00% covered (danger)
0.00%
0 / 5
72
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 / 5
0.00% covered (danger)
0.00%
0 / 1
12
 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_Pocket_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_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 Jetpack_PostImages;
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        if ( class_exists( 'Jetpack_PostImages' ) ) {
691            $image = Jetpack_PostImages::get_image( $post->ID, array( 'fallback_to_avatars' => true ) );
692            if ( ! empty( $image ) ) {
693                return $image['src'];
694            }
695        }
696
697        /**
698         * Filters the default image used by the Pinterest Pin It share button.
699         *
700         * @module sharedaddy
701         *
702         * @since 3.6.0
703         *
704         * @param string $url Default image URL.
705         */
706        return apply_filters( 'jetpack_sharing_pinterest_default_image', 'https://s0.wp.com/i/blank.jpg' );
707    }
708
709    /**
710     * Get Pinterest external sharing URL.
711     *
712     * @param WP_Post $post Post object.
713     *
714     * @return string
715     */
716    public function get_external_url( $post ) {
717        $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 );
718
719        /**
720         * Filters the Pinterest share URL used in sharing button output.
721         *
722         * @module sharedaddy
723         *
724         * @since 3.6.0
725         *
726         * @param string $url Pinterest share URL.
727         */
728        return apply_filters( 'jetpack_sharing_pinterest_share_url', $url );
729    }
730
731    /**
732     * Get Pinterest widget type.
733     *
734     * @return string
735     */
736    public function get_widget_type() {
737        /**
738         * Filters the Pinterest widget type.
739         *
740         * @see https://business.pinterest.com/en/widget-builder
741         *
742         * @module sharedaddy
743         *
744         * @since 3.6.0
745         *
746         * @param string $type Pinterest widget type. Default of 'buttonPin' for single-image selection. 'buttonBookmark' for multi-image modal.
747         */
748        return apply_filters( 'jetpack_sharing_pinterest_widget_type', 'buttonPin' );
749    }
750
751    /**
752     * Process sharing request. Add actions that need to happen when sharing here.
753     *
754     * @param WP_Post $post Post object.
755     * @param array   $post_data Array of information about the post we're sharing.
756     *
757     * @return void
758     */
759    public function process_request( $post, array $post_data ) {
760        // Record stats
761        parent::process_request( $post, $post_data );
762        // If we're triggering the multi-select panel, then we don't need to redirect to Pinterest
763        if ( ! isset( $_GET['js_only'] ) ) { // phpcs:ignore WordPress.Security.NonceVerification.Recommended
764            $pinterest_url = esc_url_raw( $this->get_external_url( $post ) );
765            parent::redirect_request( $pinterest_url );
766        } else {
767            echo '// share count bumped';
768            die( 0 );
769        }
770    }
771}
772
773/**
774 * Pocket sharing service.
775 */
776class Share_Pocket_Block extends Sharing_Source_Block {
777    /**
778     * Service short name.
779     *
780     * @var string
781     */
782    public $shortname = 'pocket';
783
784    /**
785     * Service name.
786     *
787     * @return string
788     */
789    public function get_name() {
790        return __( 'Pocket', 'jetpack' );
791    }
792
793    /**
794     * Process sharing request. Add actions that need to happen when sharing here.
795     *
796     * @param WP_Post $post Post object.
797     * @param array   $post_data Array of information about the post we're sharing.
798     *
799     * @return void
800     */
801    public function process_request( $post, array $post_data ) {
802        // Record stats
803        parent::process_request( $post, $post_data );
804
805        $pocket_url = esc_url_raw( 'https://getpocket.com/save/?url=' . rawurlencode( $this->get_share_url( $post->ID ) ) . '&title=' . rawurlencode( $this->get_share_title( $post->ID ) ) );
806
807        parent::redirect_request( $pocket_url );
808    }
809}
810
811/**
812 * Telegram sharing service.
813 */
814class Share_Telegram_Block extends Sharing_Source_Block {
815    /**
816     * Service short name.
817     *
818     * @var string
819     */
820    public $shortname = 'telegram';
821
822    /**
823     * Service name.
824     *
825     * @return string
826     */
827    public function get_name() {
828        return __( 'Telegram', 'jetpack' );
829    }
830
831    /**
832     * Process sharing request. Add actions that need to happen when sharing here.
833     *
834     * @param WP_Post $post Post object.
835     * @param array   $post_data Array of information about the post we're sharing.
836     *
837     * @return void
838     */
839    public function process_request( $post, array $post_data ) {
840        // Record stats
841        parent::process_request( $post, $post_data );
842
843        $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 ) ) );
844
845        parent::redirect_request( $telegram_url );
846    }
847}
848
849/**
850 * WhatsApp sharing service.
851 */
852class Jetpack_Share_WhatsApp_Block extends Sharing_Source_Block {
853    /**
854     * Service short name.
855     *
856     * @var string
857     */
858    public $shortname = 'jetpack-whatsapp';
859
860    /**
861     * Service name.
862     *
863     * @return string
864     */
865    public function get_name() {
866        return __( 'WhatsApp', 'jetpack' );
867    }
868
869    /**
870     * Process sharing request. Add actions that need to happen when sharing here.
871     *
872     * @param WP_Post $post Post object.
873     * @param array   $post_data Array of information about the post we're sharing.
874     *
875     * @return void
876     */
877    public function process_request( $post, array $post_data ) {
878        // Record stats
879        parent::process_request( $post, $post_data );
880
881        // Firefox for desktop doesn't handle the "api.whatsapp.com" URL properly, so use "web.whatsapp.com"
882        if ( User_Agent_Info::is_firefox_desktop() ) {
883            $url = 'https://web.whatsapp.com/send?text=';
884        } else {
885            $url = 'https://api.whatsapp.com/send?text=';
886        }
887
888        $url .= rawurlencode( $this->get_share_title( $post->ID ) . ' ' . $this->get_share_url( $post->ID ) );
889
890        parent::redirect_request( $url );
891    }
892}
893
894/**
895 * Mastodon sharing service.
896 */
897class Share_Mastodon_Block extends Sharing_Source_Block {
898    /**
899     * Service short name.
900     *
901     * @var string
902     */
903    public $shortname = 'mastodon';
904
905    /**
906     * Service name.
907     *
908     * @return string
909     */
910    public function get_name() {
911        return __( 'Mastodon', 'jetpack' );
912    }
913
914    /**
915     * Process sharing request. Add actions that need to happen when sharing here.
916     *
917     * @param WP_Post $post Post object.
918     * @param array   $post_data Array of information about the post we're sharing.
919     *
920     * @return void
921     */
922    public function process_request( $post, array $post_data ) {
923        if ( empty( $_POST['jetpack-mastodon-instance'] ) ) {
924            require_once __DIR__ . '/components/class-jetpack-mastodon-modal.php';
925            add_action( 'template_redirect', array( Jetpack_Mastodon_Modal::class, 'modal' ) );
926            return;
927        }
928
929        check_admin_referer( 'jetpack_share_mastodon_instance' );
930
931        $mastodon_instance = isset( $_POST['jetpack-mastodon-instance'] )
932            ? trailingslashit( sanitize_text_field( wp_unslash( $_POST['jetpack-mastodon-instance'] ) ) )
933            : null;
934
935        $post_title = $this->get_share_title( $post->ID );
936        $post_link  = $this->get_share_url( $post->ID );
937        $post_tags  = $this->get_share_tags( $post->ID );
938
939        /**
940         * Allow filtering the default message that gets posted to Mastodon.
941         *
942         * @module sharedaddy
943         * @since 11.9
944         *
945         * @param string  $share_url The default message that gets posted to Mastodon.
946         * @param WP_Post $post      The post object.
947         * @param array   $post_data Array of information about the post we're sharing.
948         */
949        $shared_message = apply_filters(
950            'jetpack_sharing_mastodon_default_message',
951            $post_title . ' ' . $post_link . ' ' . $post_tags,
952            $post,
953            $post_data
954        );
955
956        $share_url = sprintf(
957            '%1$sshare?text=%2$s',
958            $mastodon_instance,
959            rawurlencode( $shared_message )
960        );
961
962            // Record stats
963        parent::process_request( $post, $post_data );
964
965        parent::redirect_request( $share_url );
966    }
967}
968
969/**
970 * Nextdoor sharing service.
971 */
972class Share_Nextdoor_Block extends Sharing_Source_Block {
973    /**
974     * Service short name.
975     *
976     * @var string
977     */
978    public $shortname = 'nextdoor';
979
980    /**
981     * Service name.
982     *
983     * @return string
984     */
985    public function get_name() {
986        return __( 'Nextdoor', 'jetpack' );
987    }
988
989    /**
990     * Process sharing request. Add actions that need to happen when sharing here.
991     *
992     * @param WP_Post $post Post object.
993     * @param array   $post_data Array of information about the post we're sharing.
994     *
995     * @return void
996     */
997    public function process_request( $post, array $post_data ) {
998        // Record stats
999        parent::process_request( $post, $post_data );
1000
1001        $url  = 'https://nextdoor.com/sharekit/?source=jetpack&body=';
1002        $url .= rawurlencode( $this->get_share_title( $post->ID ) . ' ' . $this->get_share_url( $post->ID ) );
1003
1004        parent::redirect_request( $url );
1005    }
1006}
1007
1008/**
1009 * Bluesky sharing button.
1010 */
1011class Share_Bluesky_Block extends Sharing_Source_Block {
1012    /**
1013     * Service short name.
1014     *
1015     * @var string
1016     */
1017    public $shortname = 'bluesky';
1018
1019    /**
1020     * Service name.
1021     *
1022     * @return string
1023     */
1024    public function get_name() {
1025        return __( 'Bluesky', 'jetpack' );
1026    }
1027
1028    /**
1029     * Process sharing request. Add actions that need to happen when sharing here.
1030     *
1031     * @param WP_Post $post Post object.
1032     * @param array   $post_data Array of information about the post we're sharing.
1033     *
1034     * @return void
1035     */
1036    public function process_request( $post, array $post_data ) {
1037        // Record stats
1038        parent::process_request( $post, $post_data );
1039
1040        $url  = 'https://bsky.app/intent/compose?text=';
1041        $url .= rawurlencode( $this->get_share_title( $post->ID ) . ' ' . $this->get_share_url( $post->ID ) );
1042
1043        parent::redirect_request( $url );
1044    }
1045}
1046
1047/**
1048 * X sharing button.
1049 *
1050 * While the old Twitter button had an official button,
1051 * this new X button does not, since there is no official X button yet.
1052 */
1053class Share_X_Block extends Sharing_Source_Block {
1054    /**
1055     * Service short name.
1056     *
1057     * @var string
1058     */
1059    public $shortname = 'x';
1060
1061    /**
1062     * Length of a URL on X.
1063     * https://developer.twitter.com/en/docs/tco
1064     *
1065     * @var int
1066     */
1067    public $short_url_length = 24;
1068
1069    /**
1070     * Service name.
1071     *
1072     * @return string
1073     */
1074    public function get_name() {
1075        return __( 'X', 'jetpack' );
1076    }
1077
1078    /**
1079     * Determine the X 'via' value for a post.
1080     *
1081     * @param  WP_Post|int $post Post object or post ID.
1082     * @return string X handle without the preceding @.
1083     **/
1084    public static function sharing_x_via( $post ) {
1085        $post = get_post( $post );
1086        /** This filter is documented in modules/sharedaddy/sharing-sources.php */
1087        $twitter_site_tag_value = apply_filters(
1088            'jetpack_twitter_cards_site_tag',
1089            '',
1090            /** This action is documented in modules/sharedaddy/sharing-sources.php */
1091            array( 'twitter:creator' => apply_filters( 'jetpack_sharing_twitter_via', '', $post->ID ) )
1092        );
1093
1094        /*
1095         * Hack to remove the unwanted behavior of adding 'via @jetpack' which
1096         * was introduced with the adding of the Twitter cards.
1097         * This should be a temporary solution until a better method is setup.
1098         */
1099        if ( 'jetpack' === $twitter_site_tag_value ) {
1100            $twitter_site_tag_value = '';
1101        }
1102
1103        /** This filter is documented in modules/sharedaddy/sharing-sources.php */
1104        $twitter_site_tag_value = apply_filters( 'jetpack_sharing_twitter_via', $twitter_site_tag_value, $post->ID );
1105
1106        // Strip out anything other than a letter, number, or underscore.
1107        // This will prevent the inadvertent inclusion of an extra @, as well as normalizing the handle.
1108        return preg_replace( '/[^\da-z_]+/i', '', $twitter_site_tag_value );
1109    }
1110
1111    /**
1112     * Determine the 'related' X accounts for a post.
1113     *
1114     * @param  WP_Post|int $post Post object or post ID.
1115     * @return string Comma-separated list of X handles.
1116     **/
1117    public static function get_related_accounts( $post ) {
1118        $post = get_post( $post );
1119        /** This filter is documented in modules/sharedaddy/sharing-sources.php */
1120        $related_accounts = apply_filters( 'jetpack_sharing_twitter_related', array(), $post->ID );
1121
1122        // Example related string: account1,account2:Account 2 description,account3
1123        $related = array();
1124
1125        foreach ( $related_accounts as $related_account_username => $related_account_description ) {
1126            // Join the description onto the end of the username
1127            if ( $related_account_description ) {
1128                $related_account_username .= ':' . $related_account_description;
1129            }
1130
1131            $related[] = $related_account_username;
1132        }
1133
1134        return implode( ',', $related );
1135    }
1136
1137    /**
1138     * Process sharing request. Add actions that need to happen when sharing here.
1139     *
1140     * @param WP_Post $post Post object.
1141     * @param array   $post_data Array of information about the post we're sharing.
1142     *
1143     * @return void
1144     */
1145    public function process_request( $post, array $post_data ) {
1146        $post_title = $this->get_share_title( $post->ID );
1147        $post_link  = $this->get_share_url( $post->ID );
1148
1149        if ( function_exists( 'mb_stripos' ) ) {
1150            $strlen = 'mb_strlen';
1151            $substr = 'mb_substr';
1152        } else {
1153            $strlen = 'strlen';
1154            $substr = 'substr';
1155        }
1156
1157        $via     = static::sharing_x_via( $post );
1158        $related = static::get_related_accounts( $post );
1159        if ( $via ) {
1160            $sig = " via @$via";
1161            if ( $related === $via ) {
1162                $related = false;
1163            }
1164        } else {
1165            $via = false;
1166            $sig = '';
1167        }
1168
1169        $suffix_length = $this->short_url_length + $strlen( $sig );
1170        // $sig is handled by twitter in their 'via' argument.
1171        // $post_link is handled by twitter in their 'url' argument.
1172        if ( 280 < $strlen( $post_title ) + $suffix_length ) {
1173            // The -1 is for "\xE2\x80\xA6", a UTF-8 ellipsis.
1174            $text = $substr( $post_title, 0, 280 - $suffix_length - 1 ) . "\xE2\x80\xA6";
1175        } else {
1176            $text = $post_title;
1177        }
1178
1179        // Record stats
1180        parent::process_request( $post, $post_data );
1181
1182        $url         = $post_link;
1183        $twitter_url = add_query_arg(
1184            rawurlencode_deep( array_filter( compact( 'via', 'related', 'text', 'url' ) ) ),
1185            'https://x.com/intent/tweet'
1186        );
1187
1188        parent::redirect_request( $twitter_url );
1189    }
1190}
1191
1192/**
1193 * Twitter sharing button.
1194 */
1195class Share_Twitter_Block extends Sharing_Source_Block {
1196    /**
1197     * Service short name.
1198     *
1199     * @var string
1200     */
1201    public $shortname = 'twitter';
1202
1203    /**
1204     * Length of a URL on Twitter.
1205     * 'https://dev.twitter.com/rest/reference/get/help/configuration'
1206     * ( 2015/02/06 ) short_url_length is 22, short_url_length_https is 23
1207     *
1208     * @var int
1209     */
1210    public $short_url_length = 24;
1211
1212    /**
1213     * Service name.
1214     *
1215     * @return string
1216     */
1217    public function get_name() {
1218        return __( 'X', 'jetpack' );
1219    }
1220
1221    /**
1222     * Determine the Twitter 'via' value for a post.
1223     *
1224     * @param  WP_Post|int $post Post object or post ID.
1225     * @return string Twitter handle without the preceding @.
1226     **/
1227    public static function sharing_twitter_via( $post ) {
1228        $post = get_post( $post );
1229        /**
1230         * Allow third-party plugins to customize the Twitter username used as "twitter:site" Twitter Card Meta Tag.
1231         *
1232         * @module sharedaddy
1233         *
1234         * @since 3.0.0
1235         *
1236         * @param string $string Twitter Username.
1237         * @param array $args Array of Open Graph Meta Tags and Twitter Cards tags.
1238         */
1239        $twitter_site_tag_value = apply_filters(
1240            'jetpack_twitter_cards_site_tag',
1241            '',
1242            /** This action is documented in modules/sharedaddy/sharing-sources.php */
1243            array( 'twitter:creator' => apply_filters( 'jetpack_sharing_twitter_via', '', $post->ID ) )
1244        );
1245
1246        /*
1247         * Hack to remove the unwanted behavior of adding 'via @jetpack' which
1248         * was introduced with the adding of the Twitter cards.
1249         * This should be a temporary solution until a better method is setup.
1250         */
1251        if ( 'jetpack' === $twitter_site_tag_value ) {
1252            $twitter_site_tag_value = '';
1253        }
1254
1255        /**
1256         * Filters the Twitter username used as "via" in the Twitter sharing button.
1257         *
1258         * @module sharedaddy
1259         *
1260         * @since 1.7.0
1261         *
1262         * @param string $twitter_site_tag_value Twitter Username.
1263         * @param int $post->ID Post ID.
1264         */
1265        $twitter_site_tag_value = apply_filters( 'jetpack_sharing_twitter_via', $twitter_site_tag_value, $post->ID );
1266
1267        // Strip out anything other than a letter, number, or underscore.
1268        // This will prevent the inadvertent inclusion of an extra @, as well as normalizing the handle.
1269        return preg_replace( '/[^\da-z_]+/i', '', $twitter_site_tag_value );
1270    }
1271
1272    /**
1273     * Determine the 'related' Twitter accounts for a post.
1274     *
1275     * @param  WP_Post|int $post Post object or post ID.
1276     * @return string Comma-separated list of Twitter handles.
1277     **/
1278    public static function get_related_accounts( $post ) {
1279        $post = get_post( $post );
1280        /**
1281         * Filter the list of related Twitter accounts added to the Twitter sharing button.
1282         *
1283         * @module sharedaddy
1284         *
1285         * @since 1.7.0
1286         *
1287         * @param array $args Array of Twitter usernames. Format is 'username' => 'Optional description'
1288         * @param int $post->ID Post ID.
1289         */
1290        $related_accounts = apply_filters( 'jetpack_sharing_twitter_related', array(), $post->ID );
1291
1292        // Example related string: account1,account2:Account 2 description,account3
1293        $related = array();
1294
1295        foreach ( $related_accounts as $related_account_username => $related_account_description ) {
1296            // Join the description onto the end of the username
1297            if ( $related_account_description ) {
1298                $related_account_username .= ':' . $related_account_description;
1299            }
1300
1301            $related[] = $related_account_username;
1302        }
1303
1304        return implode( ',', $related );
1305    }
1306
1307    /**
1308     * Process sharing request. Add actions that need to happen when sharing here.
1309     *
1310     * @param WP_Post $post Post object.
1311     * @param array   $post_data Array of information about the post we're sharing.
1312     *
1313     * @return void
1314     */
1315    public function process_request( $post, array $post_data ) {
1316        $post_title = $this->get_share_title( $post->ID );
1317        $post_link  = $this->get_share_url( $post->ID );
1318
1319        if ( function_exists( 'mb_stripos' ) ) {
1320            $strlen = 'mb_strlen';
1321            $substr = 'mb_substr';
1322        } else {
1323            $strlen = 'strlen';
1324            $substr = 'substr';
1325        }
1326
1327        $via     = static::sharing_twitter_via( $post );
1328        $related = static::get_related_accounts( $post );
1329        if ( $via ) {
1330            $sig = " via @$via";
1331            if ( $related === $via ) {
1332                $related = false;
1333            }
1334        } else {
1335            $via = false;
1336            $sig = '';
1337        }
1338
1339        $suffix_length = $this->short_url_length + $strlen( $sig );
1340        // $sig is handled by twitter in their 'via' argument.
1341        // $post_link is handled by twitter in their 'url' argument.
1342        if ( 280 < $strlen( $post_title ) + $suffix_length ) {
1343            // The -1 is for "\xE2\x80\xA6", a UTF-8 ellipsis.
1344            $text = $substr( $post_title, 0, 280 - $suffix_length - 1 ) . "\xE2\x80\xA6";
1345        } else {
1346            $text = $post_title;
1347        }
1348
1349        // Record stats
1350        parent::process_request( $post, $post_data );
1351
1352        $url         = $post_link;
1353        $twitter_url = add_query_arg(
1354            rawurlencode_deep( array_filter( compact( 'via', 'related', 'text', 'url' ) ) ),
1355            'https://x.com/intent/tweet'
1356        );
1357
1358        parent::redirect_request( $twitter_url );
1359    }
1360}
1361
1362/**
1363 * Reddit sharing button.
1364 */
1365class Share_Reddit_Block extends Sharing_Source_Block {
1366    /**
1367     * Service short name.
1368     *
1369     * @var string
1370     */
1371    public $shortname = 'reddit';
1372
1373    /**
1374     * Service name.
1375     *
1376     * @return string
1377     */
1378    public function get_name() {
1379        return __( 'Reddit', 'jetpack' );
1380    }
1381
1382    /**
1383     * Process sharing request. Add actions that need to happen when sharing here.
1384     *
1385     * @param WP_Post $post Post object.
1386     * @param array   $post_data Array of information about the post we're sharing.
1387     *
1388     * @return void
1389     */
1390    public function process_request( $post, array $post_data ) {
1391        $reddit_url = $this->http() . '://reddit.com/submit?url=' . rawurlencode( $this->get_share_url( $post->ID ) ) . '&title=' . rawurlencode( $this->get_share_title( $post->ID ) );
1392
1393        // Record stats
1394        parent::process_request( $post, $post_data );
1395
1396        parent::redirect_request( $reddit_url );
1397    }
1398}
1399
1400/**
1401 * LinkedIn sharing button.
1402 */
1403class Share_LinkedIn_Block extends Sharing_Source_Block {
1404    /**
1405     * Service short name.
1406     *
1407     * @var string
1408     */
1409    public $shortname = 'linkedin';
1410
1411    /**
1412     * Service name.
1413     *
1414     * @return string
1415     */
1416    public function get_name() {
1417        return __( 'LinkedIn', 'jetpack' );
1418    }
1419
1420    /**
1421     * Process sharing request. Add actions that need to happen when sharing here.
1422     *
1423     * @param WP_Post $post Post object.
1424     * @param array   $post_data Array of information about the post we're sharing.
1425     *
1426     * @return void
1427     */
1428    public function process_request( $post, array $post_data ) {
1429
1430        $post_link = $this->get_share_url( $post->ID );
1431
1432        // Using the same URL as the official button, which is *not* LinkedIn's documented sharing link
1433        // https://www.linkedin.com/cws/share?url={url}&token=&isFramed=false
1434        $linkedin_url = add_query_arg(
1435            array(
1436                'url' => rawurlencode( $post_link ),
1437            ),
1438            'https://www.linkedin.com/cws/share?token=&isFramed=false'
1439        );
1440
1441        // Record stats
1442        parent::process_request( $post, $post_data );
1443
1444        parent::redirect_request( $linkedin_url );
1445    }
1446}
1447
1448/**
1449 * Threads sharing button.
1450 */
1451class Share_Threads_Block extends Sharing_Source_Block {
1452    /**
1453     * Service short name.
1454     *
1455     * @var string
1456     */
1457    public $shortname = 'threads';
1458
1459    /**
1460     * Service name.
1461     *
1462     * @return string
1463     */
1464    public function get_name() {
1465        return __( 'Threads', 'jetpack' );
1466    }
1467
1468    /**
1469     * Process sharing request. Add actions that need to happen when sharing here.
1470     *
1471     * @param WP_Post $post Post object.
1472     * @param array   $post_data Array of information about the post we're sharing.
1473     *
1474     * @return void
1475     */
1476    public function process_request( $post, array $post_data ) {
1477        // Record stats
1478        parent::process_request( $post, $post_data );
1479
1480        $url  = 'https://www.threads.net/intent/post/?text=';
1481        $url .= rawurlencode( $this->get_share_title( $post->ID ) . ' ' . $this->get_share_url( $post->ID ) );
1482
1483        parent::redirect_request( $url );
1484    }
1485}