PluginProbe
Jetpack – WP Security, Backup, Speed, & Growth / 16.3-a.1
Jetpack – WP Security, Backup, Speed, & Growth v16.3-a.1
16.3-a.1 16.2 16.2-beta 12.0.3 12.1.3 12.2.3 12.3.2 12.4.2 12.5.2 12.6.4 12.7.3 12.8.3 12.9.5 13.0.2 13.1.5 13.2.4 13.3.3 13.4.5 13.5.2 13.6.2 13.7.2 13.8.3 13.9.2 14.0.1 14.1.1 All 503 releases
jetpack / extensions / blocks / sharing-button / class-sharing-source-block.php

class-sharing-source-block.php in Jetpack – WP Security, Backup, Speed, & Growth 16.3-a.1, at extensions/blocks/sharing-button/class-sharing-source-block.php

1,442 lines 36.9 KB
No matching file
Up and down to move Enter to open Esc to close
Raw Download Zip
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
12 namespace Automattic\Jetpack\Extensions\Sharing_Button_Block;
13
14 use Automattic\Jetpack\Device_Detection\User_Agent_Info;
15 use Automattic\Jetpack\Post_Media\Images;
16 use WP_Post;
17
18 /**
19 * Base class for sharing sources.
20 * See individual sharing classes below for the implementation of this class.
21 */
22 abstract 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 */
410 class 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 never
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 }
532
533 /**
534 * Facebook sharing button.
535 */
536 class Share_Facebook_Block extends Sharing_Source_Block {
537 /**
538 * Service short name.
539 *
540 * @var string
541 */
542 public $shortname = 'facebook';
543
544 /**
545 * Sharing type.
546 *
547 * @var string
548 */
549 private $share_type = 'default';
550
551 /**
552 * Service name.
553 *
554 * @return string
555 */
556 public function get_name() {
557 return __( 'Facebook', 'jetpack' );
558 }
559
560 /**
561 * Process sharing request. Add actions that need to happen when sharing here.
562 *
563 * @param WP_Post $post Post object.
564 * @param array $post_data Array of information about the post we're sharing.
565 *
566 * @return void
567 */
568 public function process_request( $post, array $post_data ) {
569 $post_id = $post instanceof WP_Post ? $post->ID : 0;
570 $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 ) );
571
572 // Record stats
573 parent::process_request( $post, $post_data );
574
575 parent::redirect_request( $fb_url );
576 }
577 }
578
579 /**
580 * Print button.
581 */
582 class Share_Print_Block extends Sharing_Source_Block {
583 /**
584 * Service short name.
585 *
586 * @var string
587 */
588 public $shortname = 'print';
589
590 /**
591 * Service name.
592 *
593 * @return string
594 */
595 public function get_name() {
596 return __( 'Print', 'jetpack' );
597 }
598 }
599
600 /**
601 * Native Share button (relying on the Web Share API).
602 */
603 class Share_Native_Block extends Sharing_Source_Block {
604 /**
605 * Service short name.
606 *
607 * @var string
608 */
609 public $shortname = 'native';
610
611 /**
612 * Service name.
613 *
614 * @return string
615 */
616 public function get_name() {
617 return __( 'Web Share', 'jetpack' );
618 }
619 }
620
621 /**
622 * Tumblr sharing service.
623 */
624 class Share_Tumblr_Block extends Sharing_Source_Block {
625 /**
626 * Service short name.
627 *
628 * @var string
629 */
630 public $shortname = 'tumblr';
631
632 /**
633 * Service name.
634 *
635 * @return string
636 */
637 public function get_name() {
638 return __( 'Tumblr', 'jetpack' );
639 }
640
641 /**
642 * Process sharing request. Add actions that need to happen when sharing here.
643 *
644 * @param WP_Post $post Post object.
645 * @param array $post_data Array of information about the post we're sharing.
646 *
647 * @return void
648 */
649 public function process_request( $post, array $post_data ) {
650 // Record stats
651 parent::process_request( $post, $post_data );
652
653 // Redirect to Tumblr's sharing endpoint (a la their bookmarklet)
654 $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=';
655
656 parent::redirect_request( $url );
657 }
658 }
659
660 /**
661 * Pinterest sharing service.
662 */
663 class Share_Pinterest_Block extends Sharing_Source_Block {
664 /**
665 * Service short name.
666 *
667 * @var string
668 */
669 public $shortname = 'pinterest';
670
671 /**
672 * Service name.
673 *
674 * @return string
675 */
676 public function get_name() {
677 return __( 'Pinterest', 'jetpack' );
678 }
679
680 /**
681 * Get image representative of the post to pass on to Pinterest.
682 *
683 * @param WP_Post $post Post object.
684 *
685 * @return string
686 */
687 public function get_image( $post ) {
688 $image = Images::get_image( $post->ID, array( 'fallback_to_avatars' => true ) );
689 if ( ! empty( $image ) ) {
690 return $image['src'];
691 }
692
693 /**
694 * Filters the default image used by the Pinterest Pin It share button.
695 *
696 * @module sharedaddy
697 *
698 * @since 3.6.0
699 *
700 * @param string $url Default image URL.
701 */
702 return apply_filters( 'jetpack_sharing_pinterest_default_image', 'https://s0.wp.com/i/blank.jpg' );
703 }
704
705 /**
706 * Get Pinterest external sharing URL.
707 *
708 * @param WP_Post $post Post object.
709 *
710 * @return string
711 */
712 public function get_external_url( $post ) {
713 $url = 'https://www.pinterest.com/pin/create/link/?url=' . rawurlencode( $this->get_share_url( $post->ID ) ) . '&media=' . rawurlencode( $this->get_image( $post ) ) . '&description=' . rawurlencode( $post->post_title );
714
715 /**
716 * Filters the Pinterest share URL used in sharing button output.
717 *
718 * @module sharedaddy
719 *
720 * @since 3.6.0
721 *
722 * @param string $url Pinterest share URL.
723 */
724 return apply_filters( 'jetpack_sharing_pinterest_share_url', $url );
725 }
726
727 /**
728 * Get Pinterest widget type.
729 *
730 * @return string
731 */
732 public function get_widget_type() {
733 /**
734 * Filters the Pinterest widget type.
735 *
736 * @see https://business.pinterest.com/en/widget-builder
737 *
738 * @module sharedaddy
739 *
740 * @since 3.6.0
741 *
742 * @param string $type Pinterest widget type. Default of 'buttonPin' for single-image selection. 'buttonBookmark' for multi-image modal.
743 */
744 return apply_filters( 'jetpack_sharing_pinterest_widget_type', 'buttonPin' );
745 }
746
747 /**
748 * Process sharing request. Add actions that need to happen when sharing here.
749 *
750 * @param WP_Post $post Post object.
751 * @param array $post_data Array of information about the post we're sharing.
752 *
753 * @return void
754 */
755 public function process_request( $post, array $post_data ) {
756 // Record stats
757 parent::process_request( $post, $post_data );
758 // If we're triggering the multi-select panel, then we don't need to redirect to Pinterest
759 if ( ! isset( $_GET['js_only'] ) ) { // phpcs:ignore WordPress.Security.NonceVerification.Recommended
760 $pinterest_url = esc_url_raw( $this->get_external_url( $post ) );
761 parent::redirect_request( $pinterest_url );
762 } else {
763 echo '// share count bumped';
764 die( 0 );
765 }
766 }
767 }
768
769 /**
770 * Telegram sharing service.
771 */
772 class Share_Telegram_Block extends Sharing_Source_Block {
773 /**
774 * Service short name.
775 *
776 * @var string
777 */
778 public $shortname = 'telegram';
779
780 /**
781 * Service name.
782 *
783 * @return string
784 */
785 public function get_name() {
786 return __( 'Telegram', 'jetpack' );
787 }
788
789 /**
790 * Process sharing request. Add actions that need to happen when sharing here.
791 *
792 * @param WP_Post $post Post object.
793 * @param array $post_data Array of information about the post we're sharing.
794 *
795 * @return void
796 */
797 public function process_request( $post, array $post_data ) {
798 // Record stats
799 parent::process_request( $post, $post_data );
800
801 $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 ) ) );
802
803 parent::redirect_request( $telegram_url );
804 }
805 }
806
807 /**
808 * WhatsApp sharing service.
809 */
810 class Jetpack_Share_WhatsApp_Block extends Sharing_Source_Block {
811 /**
812 * Service short name.
813 *
814 * @var string
815 */
816 public $shortname = 'jetpack-whatsapp';
817
818 /**
819 * Service name.
820 *
821 * @return string
822 */
823 public function get_name() {
824 return __( 'WhatsApp', 'jetpack' );
825 }
826
827 /**
828 * Process sharing request. Add actions that need to happen when sharing here.
829 *
830 * @param WP_Post $post Post object.
831 * @param array $post_data Array of information about the post we're sharing.
832 *
833 * @return void
834 */
835 public function process_request( $post, array $post_data ) {
836 // Record stats
837 parent::process_request( $post, $post_data );
838
839 // Firefox for desktop doesn't handle the "api.whatsapp.com" URL properly, so use "web.whatsapp.com"
840 if ( User_Agent_Info::is_firefox_desktop() ) {
841 $url = 'https://web.whatsapp.com/send?text=';
842 } else {
843 $url = 'https://api.whatsapp.com/send?text=';
844 }
845
846 $url .= rawurlencode( $this->get_share_title( $post->ID ) . ' ' . $this->get_share_url( $post->ID ) );
847
848 parent::redirect_request( $url );
849 }
850 }
851
852 /**
853 * Mastodon sharing service.
854 */
855 class Share_Mastodon_Block extends Sharing_Source_Block {
856 /**
857 * Service short name.
858 *
859 * @var string
860 */
861 public $shortname = 'mastodon';
862
863 /**
864 * Service name.
865 *
866 * @return string
867 */
868 public function get_name() {
869 return __( 'Mastodon', 'jetpack' );
870 }
871
872 /**
873 * Process sharing request. Add actions that need to happen when sharing here.
874 *
875 * @param WP_Post $post Post object.
876 * @param array $post_data Array of information about the post we're sharing.
877 *
878 * @return void
879 */
880 public function process_request( $post, array $post_data ) {
881 if ( empty( $_POST['jetpack-mastodon-instance'] ) ) {
882 require_once __DIR__ . '/components/class-jetpack-mastodon-modal.php';
883 add_action( 'template_redirect', array( Jetpack_Mastodon_Modal::class, 'modal' ) );
884 return;
885 }
886
887 check_admin_referer( 'jetpack_share_mastodon_instance' );
888
889 $mastodon_instance = isset( $_POST['jetpack-mastodon-instance'] )
890 ? trailingslashit( sanitize_text_field( wp_unslash( $_POST['jetpack-mastodon-instance'] ) ) )
891 : null;
892
893 $post_title = $this->get_share_title( $post->ID );
894 $post_link = $this->get_share_url( $post->ID );
895 $post_tags = $this->get_share_tags( $post->ID );
896
897 /**
898 * Allow filtering the default message that gets posted to Mastodon.
899 *
900 * @module sharedaddy
901 * @since 11.9
902 *
903 * @param string $share_url The default message that gets posted to Mastodon.
904 * @param WP_Post $post The post object.
905 * @param array $post_data Array of information about the post we're sharing.
906 */
907 $shared_message = apply_filters(
908 'jetpack_sharing_mastodon_default_message',
909 $post_title . ' ' . $post_link . ' ' . $post_tags,
910 $post,
911 $post_data
912 );
913
914 $share_url = sprintf(
915 '%1$sshare?text=%2$s',
916 $mastodon_instance,
917 rawurlencode( $shared_message )
918 );
919
920 // Record stats
921 parent::process_request( $post, $post_data );
922
923 parent::redirect_request( $share_url );
924 }
925 }
926
927 /**
928 * Nextdoor sharing service.
929 */
930 class Share_Nextdoor_Block extends Sharing_Source_Block {
931 /**
932 * Service short name.
933 *
934 * @var string
935 */
936 public $shortname = 'nextdoor';
937
938 /**
939 * Service name.
940 *
941 * @return string
942 */
943 public function get_name() {
944 return __( 'Nextdoor', 'jetpack' );
945 }
946
947 /**
948 * Process sharing request. Add actions that need to happen when sharing here.
949 *
950 * @param WP_Post $post Post object.
951 * @param array $post_data Array of information about the post we're sharing.
952 *
953 * @return void
954 */
955 public function process_request( $post, array $post_data ) {
956 // Record stats
957 parent::process_request( $post, $post_data );
958
959 $url = 'https://nextdoor.com/sharekit/?source=jetpack&body=';
960 $url .= rawurlencode( $this->get_share_title( $post->ID ) . ' ' . $this->get_share_url( $post->ID ) );
961
962 parent::redirect_request( $url );
963 }
964 }
965
966 /**
967 * Bluesky sharing button.
968 */
969 class Share_Bluesky_Block extends Sharing_Source_Block {
970 /**
971 * Service short name.
972 *
973 * @var string
974 */
975 public $shortname = 'bluesky';
976
977 /**
978 * Service name.
979 *
980 * @return string
981 */
982 public function get_name() {
983 return __( 'Bluesky', 'jetpack' );
984 }
985
986 /**
987 * Process sharing request. Add actions that need to happen when sharing here.
988 *
989 * @param WP_Post $post Post object.
990 * @param array $post_data Array of information about the post we're sharing.
991 *
992 * @return void
993 */
994 public function process_request( $post, array $post_data ) {
995 // Record stats
996 parent::process_request( $post, $post_data );
997
998 $url = 'https://bsky.app/intent/compose?text=';
999 $url .= rawurlencode( $this->get_share_title( $post->ID ) . ' ' . $this->get_share_url( $post->ID ) );
1000
1001 parent::redirect_request( $url );
1002 }
1003 }
1004
1005 /**
1006 * X sharing button.
1007 *
1008 * While the old Twitter button had an official button,
1009 * this new X button does not, since there is no official X button yet.
1010 */
1011 class Share_X_Block extends Sharing_Source_Block {
1012 /**
1013 * Service short name.
1014 *
1015 * @var string
1016 */
1017 public $shortname = 'x';
1018
1019 /**
1020 * Length of a URL on X.
1021 * https://developer.twitter.com/en/docs/tco
1022 *
1023 * @var int
1024 */
1025 public $short_url_length = 24;
1026
1027 /**
1028 * Service name.
1029 *
1030 * @return string
1031 */
1032 public function get_name() {
1033 return __( 'X', 'jetpack' );
1034 }
1035
1036 /**
1037 * Determine the X 'via' value for a post.
1038 *
1039 * @param WP_Post|int $post Post object or post ID.
1040 * @return string X handle without the preceding @.
1041 **/
1042 public static function sharing_x_via( $post ) {
1043 $post = get_post( $post );
1044 /** This filter is documented in modules/sharedaddy/sharing-sources.php */
1045 $twitter_site_tag_value = apply_filters(
1046 'jetpack_twitter_cards_site_tag',
1047 '',
1048 /** This action is documented in modules/sharedaddy/sharing-sources.php */
1049 array( 'twitter:creator' => apply_filters( 'jetpack_sharing_twitter_via', '', $post->ID ) )
1050 );
1051
1052 /*
1053 * Hack to remove the unwanted behavior of adding 'via @jetpack' which
1054 * was introduced with the adding of the Twitter cards.
1055 * This should be a temporary solution until a better method is setup.
1056 */
1057 if ( 'jetpack' === $twitter_site_tag_value ) {
1058 $twitter_site_tag_value = '';
1059 }
1060
1061 /** This filter is documented in modules/sharedaddy/sharing-sources.php */
1062 $twitter_site_tag_value = apply_filters( 'jetpack_sharing_twitter_via', $twitter_site_tag_value, $post->ID );
1063
1064 // Strip out anything other than a letter, number, or underscore.
1065 // This will prevent the inadvertent inclusion of an extra @, as well as normalizing the handle.
1066 return preg_replace( '/[^\da-z_]+/i', '', $twitter_site_tag_value );
1067 }
1068
1069 /**
1070 * Determine the 'related' X accounts for a post.
1071 *
1072 * @param WP_Post|int $post Post object or post ID.
1073 * @return string Comma-separated list of X handles.
1074 **/
1075 public static function get_related_accounts( $post ) {
1076 $post = get_post( $post );
1077 /** This filter is documented in modules/sharedaddy/sharing-sources.php */
1078 $related_accounts = apply_filters( 'jetpack_sharing_twitter_related', array(), $post->ID );
1079
1080 // Example related string: account1,account2:Account 2 description,account3
1081 $related = array();
1082
1083 foreach ( $related_accounts as $related_account_username => $related_account_description ) {
1084 // Join the description onto the end of the username
1085 if ( $related_account_description ) {
1086 $related_account_username .= ':' . $related_account_description;
1087 }
1088
1089 $related[] = $related_account_username;
1090 }
1091
1092 return implode( ',', $related );
1093 }
1094
1095 /**
1096 * Process sharing request. Add actions that need to happen when sharing here.
1097 *
1098 * @param WP_Post $post Post object.
1099 * @param array $post_data Array of information about the post we're sharing.
1100 *
1101 * @return void
1102 */
1103 public function process_request( $post, array $post_data ) {
1104 $post_title = $this->get_share_title( $post->ID );
1105 $post_link = $this->get_share_url( $post->ID );
1106
1107 if ( function_exists( 'mb_stripos' ) ) {
1108 $strlen = 'mb_strlen';
1109 $substr = 'mb_substr';
1110 } else {
1111 $strlen = 'strlen';
1112 $substr = 'substr';
1113 }
1114
1115 $via = static::sharing_x_via( $post );
1116 $related = static::get_related_accounts( $post );
1117 if ( $via ) {
1118 $sig = " via @$via";
1119 if ( $related === $via ) {
1120 $related = false;
1121 }
1122 } else {
1123 $via = false;
1124 $sig = '';
1125 }
1126
1127 $suffix_length = $this->short_url_length + $strlen( $sig );
1128 // $sig is handled by twitter in their 'via' argument.
1129 // $post_link is handled by twitter in their 'url' argument.
1130 if ( 280 < $strlen( $post_title ) + $suffix_length ) {
1131 // The -1 is for "\xE2\x80\xA6", a UTF-8 ellipsis.
1132 $text = $substr( $post_title, 0, 280 - $suffix_length - 1 ) . "\xE2\x80\xA6";
1133 } else {
1134 $text = $post_title;
1135 }
1136
1137 // Record stats
1138 parent::process_request( $post, $post_data );
1139
1140 $url = $post_link;
1141 $twitter_url = add_query_arg(
1142 rawurlencode_deep( array_filter( compact( 'via', 'related', 'text', 'url' ) ) ),
1143 'https://x.com/intent/tweet'
1144 );
1145
1146 parent::redirect_request( $twitter_url );
1147 }
1148 }
1149
1150 /**
1151 * Twitter sharing button.
1152 */
1153 class Share_Twitter_Block extends Sharing_Source_Block {
1154 /**
1155 * Service short name.
1156 *
1157 * @var string
1158 */
1159 public $shortname = 'twitter';
1160
1161 /**
1162 * Length of a URL on Twitter.
1163 * 'https://dev.twitter.com/rest/reference/get/help/configuration'
1164 * ( 2015/02/06 ) short_url_length is 22, short_url_length_https is 23
1165 *
1166 * @var int
1167 */
1168 public $short_url_length = 24;
1169
1170 /**
1171 * Service name.
1172 *
1173 * @return string
1174 */
1175 public function get_name() {
1176 return __( 'X', 'jetpack' );
1177 }
1178
1179 /**
1180 * Determine the Twitter 'via' value for a post.
1181 *
1182 * @param WP_Post|int $post Post object or post ID.
1183 * @return string Twitter handle without the preceding @.
1184 **/
1185 public static function sharing_twitter_via( $post ) {
1186 $post = get_post( $post );
1187 /**
1188 * Allow third-party plugins to customize the Twitter username used as "twitter:site" Twitter Card Meta Tag.
1189 *
1190 * @module sharedaddy
1191 *
1192 * @since 3.0.0
1193 *
1194 * @param string $string Twitter Username.
1195 * @param array $args Array of Open Graph Meta Tags and Twitter Cards tags.
1196 */
1197 $twitter_site_tag_value = apply_filters(
1198 'jetpack_twitter_cards_site_tag',
1199 '',
1200 /** This action is documented in modules/sharedaddy/sharing-sources.php */
1201 array( 'twitter:creator' => apply_filters( 'jetpack_sharing_twitter_via', '', $post->ID ) )
1202 );
1203
1204 /*
1205 * Hack to remove the unwanted behavior of adding 'via @jetpack' which
1206 * was introduced with the adding of the Twitter cards.
1207 * This should be a temporary solution until a better method is setup.
1208 */
1209 if ( 'jetpack' === $twitter_site_tag_value ) {
1210 $twitter_site_tag_value = '';
1211 }
1212
1213 /**
1214 * Filters the Twitter username used as "via" in the Twitter sharing button.
1215 *
1216 * @module sharedaddy
1217 *
1218 * @since 1.7.0
1219 *
1220 * @param string $twitter_site_tag_value Twitter Username.
1221 * @param int $post->ID Post ID.
1222 */
1223 $twitter_site_tag_value = apply_filters( 'jetpack_sharing_twitter_via', $twitter_site_tag_value, $post->ID );
1224
1225 // Strip out anything other than a letter, number, or underscore.
1226 // This will prevent the inadvertent inclusion of an extra @, as well as normalizing the handle.
1227 return preg_replace( '/[^\da-z_]+/i', '', $twitter_site_tag_value );
1228 }
1229
1230 /**
1231 * Determine the 'related' Twitter accounts for a post.
1232 *
1233 * @param WP_Post|int $post Post object or post ID.
1234 * @return string Comma-separated list of Twitter handles.
1235 **/
1236 public static function get_related_accounts( $post ) {
1237 $post = get_post( $post );
1238 /**
1239 * Filter the list of related Twitter accounts added to the Twitter sharing button.
1240 *
1241 * @module sharedaddy
1242 *
1243 * @since 1.7.0
1244 *
1245 * @param array $args Array of Twitter usernames. Format is 'username' => 'Optional description'
1246 * @param int $post->ID Post ID.
1247 */
1248 $related_accounts = apply_filters( 'jetpack_sharing_twitter_related', array(), $post->ID );
1249
1250 // Example related string: account1,account2:Account 2 description,account3
1251 $related = array();
1252
1253 foreach ( $related_accounts as $related_account_username => $related_account_description ) {
1254 // Join the description onto the end of the username
1255 if ( $related_account_description ) {
1256 $related_account_username .= ':' . $related_account_description;
1257 }
1258
1259 $related[] = $related_account_username;
1260 }
1261
1262 return implode( ',', $related );
1263 }
1264
1265 /**
1266 * Process sharing request. Add actions that need to happen when sharing here.
1267 *
1268 * @param WP_Post $post Post object.
1269 * @param array $post_data Array of information about the post we're sharing.
1270 *
1271 * @return void
1272 */
1273 public function process_request( $post, array $post_data ) {
1274 $post_title = $this->get_share_title( $post->ID );
1275 $post_link = $this->get_share_url( $post->ID );
1276
1277 if ( function_exists( 'mb_stripos' ) ) {
1278 $strlen = 'mb_strlen';
1279 $substr = 'mb_substr';
1280 } else {
1281 $strlen = 'strlen';
1282 $substr = 'substr';
1283 }
1284
1285 $via = static::sharing_twitter_via( $post );
1286 $related = static::get_related_accounts( $post );
1287 if ( $via ) {
1288 $sig = " via @$via";
1289 if ( $related === $via ) {
1290 $related = false;
1291 }
1292 } else {
1293 $via = false;
1294 $sig = '';
1295 }
1296
1297 $suffix_length = $this->short_url_length + $strlen( $sig );
1298 // $sig is handled by twitter in their 'via' argument.
1299 // $post_link is handled by twitter in their 'url' argument.
1300 if ( 280 < $strlen( $post_title ) + $suffix_length ) {
1301 // The -1 is for "\xE2\x80\xA6", a UTF-8 ellipsis.
1302 $text = $substr( $post_title, 0, 280 - $suffix_length - 1 ) . "\xE2\x80\xA6";
1303 } else {
1304 $text = $post_title;
1305 }
1306
1307 // Record stats
1308 parent::process_request( $post, $post_data );
1309
1310 $url = $post_link;
1311 $twitter_url = add_query_arg(
1312 rawurlencode_deep( array_filter( compact( 'via', 'related', 'text', 'url' ) ) ),
1313 'https://x.com/intent/tweet'
1314 );
1315
1316 parent::redirect_request( $twitter_url );
1317 }
1318 }
1319
1320 /**
1321 * Reddit sharing button.
1322 */
1323 class Share_Reddit_Block extends Sharing_Source_Block {
1324 /**
1325 * Service short name.
1326 *
1327 * @var string
1328 */
1329 public $shortname = 'reddit';
1330
1331 /**
1332 * Service name.
1333 *
1334 * @return string
1335 */
1336 public function get_name() {
1337 return __( 'Reddit', 'jetpack' );
1338 }
1339
1340 /**
1341 * Process sharing request. Add actions that need to happen when sharing here.
1342 *
1343 * @param WP_Post $post Post object.
1344 * @param array $post_data Array of information about the post we're sharing.
1345 *
1346 * @return void
1347 */
1348 public function process_request( $post, array $post_data ) {
1349 $reddit_url = $this->http() . '://reddit.com/submit?url=' . rawurlencode( $this->get_share_url( $post->ID ) ) . '&title=' . rawurlencode( $this->get_share_title( $post->ID ) );
1350
1351 // Record stats
1352 parent::process_request( $post, $post_data );
1353
1354 parent::redirect_request( $reddit_url );
1355 }
1356 }
1357
1358 /**
1359 * LinkedIn sharing button.
1360 */
1361 class Share_LinkedIn_Block extends Sharing_Source_Block {
1362 /**
1363 * Service short name.
1364 *
1365 * @var string
1366 */
1367 public $shortname = 'linkedin';
1368
1369 /**
1370 * Service name.
1371 *
1372 * @return string
1373 */
1374 public function get_name() {
1375 return __( 'LinkedIn', 'jetpack' );
1376 }
1377
1378 /**
1379 * Process sharing request. Add actions that need to happen when sharing here.
1380 *
1381 * @param WP_Post $post Post object.
1382 * @param array $post_data Array of information about the post we're sharing.
1383 *
1384 * @return void
1385 */
1386 public function process_request( $post, array $post_data ) {
1387
1388 $post_link = $this->get_share_url( $post->ID );
1389
1390 $linkedin_url = add_query_arg(
1391 array(
1392 'url' => rawurlencode( $post_link ),
1393 ),
1394 'https://www.linkedin.com/sharing/share-offsite/'
1395 );
1396
1397 // Record stats
1398 parent::process_request( $post, $post_data );
1399
1400 parent::redirect_request( $linkedin_url );
1401 }
1402 }
1403
1404 /**
1405 * Threads sharing button.
1406 */
1407 class Share_Threads_Block extends Sharing_Source_Block {
1408 /**
1409 * Service short name.
1410 *
1411 * @var string
1412 */
1413 public $shortname = 'threads';
1414
1415 /**
1416 * Service name.
1417 *
1418 * @return string
1419 */
1420 public function get_name() {
1421 return __( 'Threads', 'jetpack' );
1422 }
1423
1424 /**
1425 * Process sharing request. Add actions that need to happen when sharing here.
1426 *
1427 * @param WP_Post $post Post object.
1428 * @param array $post_data Array of information about the post we're sharing.
1429 *
1430 * @return void
1431 */
1432 public function process_request( $post, array $post_data ) {
1433 // Record stats
1434 parent::process_request( $post, $post_data );
1435
1436 $url = 'https://www.threads.net/intent/post/?text=';
1437 $url .= rawurlencode( $this->get_share_title( $post->ID ) . ' ' . $this->get_share_url( $post->ID ) );
1438
1439 parent::redirect_request( $url );
1440 }
1441 }
1442