PluginProbe
Jetpack – WP Security, Backup, Speed, & Growth / 14.9
Jetpack – WP Security, Backup, Speed, & Growth v14.9
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 14.2.2 14.3.1 All 501 releases
jetpack / modules / sharedaddy / sharing-sources.php

sharing-sources.php in Jetpack – WP Security, Backup, Speed, & Growth 14.9, at modules/sharedaddy/sharing-sources.php

3,523 lines 88.9 KB
No matching file
Up and down to move Enter to open Esc to close
Raw Download Zip
1 <?php // phpcs:ignore WordPress.Files.FileName.InvalidClassFileName
2 /**
3 * Define all sharing sources.
4 *
5 * phpcs:disable Generic.Files.OneObjectStructurePerFile.MultipleFound
6 */
7
8 use Automattic\Jetpack\Device_Detection\User_Agent_Info;
9
10 /**
11 * Base class for sharing sources.
12 * See individual sharing classes below for the implementation of this class.
13 */
14 abstract class Sharing_Source {
15 /**
16 * Button style (icon, icon-text, text, or official).
17 *
18 * @var string
19 */
20 public $button_style;
21
22 /**
23 * Does the service have an official version.
24 *
25 * @var bool
26 */
27 public $smart;
28
29 /**
30 * Service short name.
31 *
32 * @var string
33 */
34 public $shortname;
35
36 /**
37 * Should the sharing link open in a new tab.
38 *
39 * @var bool
40 */
41 protected $open_link_in_new;
42
43 /**
44 * Sharing unique ID.
45 *
46 * @var int
47 */
48 protected $id;
49
50 /**
51 * Constructor.
52 *
53 * @param int $id Sharing source ID.
54 * @param array $settings Sharing settings.
55 */
56 public function __construct( $id, array $settings ) {
57 $this->id = $id;
58 /**
59 * Filter the way sharing links open.
60 *
61 * By default, sharing links open in a new window.
62 *
63 * @module sharedaddy
64 *
65 * @since 3.4.0
66 *
67 * @param bool true Should Sharing links open in a new window. Default to true.
68 */
69 $this->open_link_in_new = apply_filters( 'jetpack_open_sharing_in_new_window', true );
70
71 if ( isset( $settings['button_style'] ) ) {
72 $this->button_style = $settings['button_style'];
73 }
74
75 if ( isset( $settings['smart'] ) ) {
76 $this->smart = $settings['smart'];
77 }
78 }
79
80 /**
81 * Is a service deprecated.
82 *
83 * @return bool
84 */
85 public function is_deprecated() {
86 return false;
87 }
88
89 /**
90 * Get the protocol to use for a sharing service, based on the site settings.
91 *
92 * @return string
93 */
94 public function http() {
95 return is_ssl() ? 'https' : 'http';
96 }
97
98 /**
99 * Get unique sharing ID.
100 *
101 * @return int
102 */
103 public function get_id() {
104 return $this->id;
105 }
106
107 /**
108 * Get unique sharing ID. Similar to get_id().
109 *
110 * @return int
111 */
112 public function get_class() {
113 return $this->id;
114 }
115
116 /**
117 * Get a post's permalink to use for sharing.
118 *
119 * @param int $post_id Post ID.
120 *
121 * @return string
122 */
123 public function get_share_url( $post_id ) {
124 /**
125 * Filter the sharing permalink.
126 *
127 * @module sharedaddy
128 *
129 * @since 1.2.0
130 *
131 * @param string get_permalink( $post_id ) Post Permalink.
132 * @param int $post_id Post ID.
133 * @param int $this->id Sharing ID.
134 */
135 return apply_filters( 'sharing_permalink', get_permalink( $post_id ), $post_id, $this->id );
136 }
137
138 /**
139 * Get a post's title to use for sharing.
140 *
141 * @param int $post_id Post ID.
142 *
143 * @return string
144 */
145 public function get_share_title( $post_id ) {
146 $post = get_post( $post_id );
147 /**
148 * Filter the sharing title.
149 *
150 * @module sharedaddy
151 *
152 * @since 2.8.0
153 *
154 * @param string $post->post_title Post Title.
155 * @param int $post_id Post ID.
156 * @param int $this->id Sharing ID.
157 */
158 $title = apply_filters( 'sharing_title', $post->post_title, $post_id, $this->id );
159
160 return html_entity_decode( wp_kses( $title, '' ), ENT_QUOTES | ENT_SUBSTITUTE | ENT_HTML401 );
161 }
162
163 /**
164 * Get a comma-separated list of the post's tags to use for sharing.
165 * Prepends a '#' to each tag.
166 *
167 * @param int $post_id Post ID.
168 *
169 * @return string
170 */
171 public function get_share_tags( $post_id ) {
172 $tags = get_the_tags( $post_id );
173 if ( ! $tags ) {
174 return '';
175 }
176
177 $tags = array_map(
178 function ( $tag ) {
179 // Camel case the tag name and remove spaces as well as apostrophes.
180 $tag = preg_replace( '/\s+|\'/', '', ucwords( $tag->name ) );
181
182 // Return with a '#' prepended.
183 return '#' . $tag;
184 },
185 $tags
186 );
187
188 /**
189 * Allow customizing how the list of tags is displayed.
190 *
191 * @module sharedaddy
192 * @since 11.9
193 *
194 * @param string $tags Comma-separated list of tags.
195 * @param int $post_id Post ID.
196 * @param int $this->id Sharing ID.
197 */
198 $tag_list = (string) apply_filters( 'jetpack_sharing_tag_list', implode( ', ', $tags ), $post_id, $this->id );
199
200 return html_entity_decode( wp_kses( $tag_list, '' ), ENT_QUOTES | ENT_SUBSTITUTE | ENT_HTML401 );
201 }
202
203 /**
204 * Does this sharing source have a custom style.
205 *
206 * @return bool
207 */
208 public function has_custom_button_style() {
209 return false;
210 }
211
212 /**
213 * Get the HTML markup to display a sharing link.
214 *
215 * @param string $url Post URL to share.
216 * @param string $text Sharing display text.
217 * @param string $accessible_name Accessible name for the link.
218 * @param string $query Additional query arguments to add to the link. They should be in 'foo=bar&baz=1' format.
219 * @param bool|string $id Sharing ID to include in the data-shared attribute.
220 * @param array $data_attributes The keys are used as additional attribute names with 'data-' prefix.
221 * The values are used as the attribute values.
222 *
223 * @return string The HTML for the link.
224 */
225 public function get_link( $url, $text, $accessible_name = '', $query = '', $id = false, $data_attributes = array() ) {
226 $args = func_get_args();
227 $klasses = array( 'share-' . $this->get_class(), 'sd-button' );
228
229 if ( 'icon' === $this->button_style || 'icon-text' === $this->button_style ) {
230 $klasses[] = 'share-icon';
231 }
232
233 if ( 'icon' === $this->button_style ) {
234 $klasses[] = 'no-text';
235 }
236
237 if ( true === $this->open_link_in_new ) {
238 $accessible_name .= __( ' (Opens in new window)', 'jetpack' );
239 }
240
241 /**
242 * Filter the sharing display ID.
243 *
244 * @module sharedaddy
245 *
246 * @since 3.4.0
247 *
248 * @param string|false $id Sharing ID.
249 * @param object $this Sharing service properties.
250 * @param array $args Array of sharing service options.
251 */
252 $id = apply_filters( 'jetpack_sharing_display_id', $id, $this, $args );
253 /**
254 * Filter the sharing display link.
255 *
256 * @module sharedaddy
257 *
258 * @since 2.8.0
259 *
260 * @param string $url Post URL.
261 * @param object $this Sharing service properties.
262 * @param string|false $id Sharing ID.
263 * @param array $args Array of sharing service options.
264 */
265 $url = apply_filters( 'sharing_display_link', $url, $this, $id, $args ); // backwards compatibility
266 /**
267 * Filter the sharing display link.
268 *
269 * @module sharedaddy
270 *
271 * @since 2.8.0
272 *
273 * @param string $url Post URL.
274 * @param object $this Sharing service properties.
275 * @param string|false $id Sharing ID.
276 * @param array $args Array of sharing service options.
277 */
278 $url = apply_filters( 'jetpack_sharing_display_link', $url, $this, $id, $args );
279 /**
280 * Filter the sharing display query.
281 *
282 * @module sharedaddy
283 *
284 * @since 2.8.0
285 *
286 * @param string $query Sharing service URL parameter.
287 * @param object $this Sharing service properties.
288 * @param string|false $id Sharing ID.
289 * @param array $args Array of sharing service options.
290 */
291 $query = apply_filters( 'jetpack_sharing_display_query', $query, $this, $id, $args );
292
293 if ( ! empty( $query ) ) {
294 if ( false === stripos( $url, '?' ) ) {
295 $url .= '?' . $query;
296 } else {
297 $url .= '&amp;' . $query;
298 }
299 }
300
301 // @phan-suppress-next-line PhanSuspiciousValueComparison
302 if ( 'text' === $this->button_style ) {
303 $klasses[] = 'no-icon';
304 }
305
306 /**
307 * Filter the sharing display classes.
308 *
309 * @module sharedaddy
310 *
311 * @since 3.4.0
312 *
313 * @param array $klasses Sharing service classes.
314 * @param object $this Sharing service properties.
315 * @param string|false $id Sharing ID.
316 * @param array $args Array of sharing service options.
317 */
318 $klasses = apply_filters( 'jetpack_sharing_display_classes', $klasses, $this, $id, $args );
319 /**
320 * Filter the sharing display title.
321 *
322 * @module sharedaddy
323 *
324 * @since 3.4.0
325 * @deprecated 14.6 Use jetpack_sharing_accessible_name instead.
326 *
327 * @param string $title Sharing service title.
328 * @param object $this Sharing service properties.
329 * @param string|false $id Sharing ID.
330 * @param array $args Array of sharing service options.
331 */
332 $accessible_name = apply_filters_deprecated( 'jetpack_sharing_display_title', array( $accessible_name, $this, $id, $args ), '14.6', 'jetpack_sharing_accessible_name' );
333
334 /**
335 * Filter the sharing accessible name.
336 *
337 * @module sharedaddy
338 *
339 * @since 14.6
340 *
341 * @param string $accessible_name Sharing service accessible name.
342 * @param object $this Sharing service properties.
343 * @param string|false $id Sharing ID.
344 * @param array $args Array of sharing service options.
345 */
346 $accessible_name = apply_filters( 'jetpack_sharing_accessible_name', $accessible_name, $this, $id, $args );
347 /**
348 * Filter the sharing display text.
349 *
350 * @module sharedaddy
351 *
352 * @since 3.4.0
353 *
354 * @param string $text Sharing service text.
355 * @param object $this Sharing service properties.
356 * @param string|false $id Sharing ID.
357 * @param array $args Array of sharing service options.
358 */
359 $text = apply_filters( 'jetpack_sharing_display_text', $text, $this, $id, $args );
360
361 /**
362 * Filter the sharing data attributes.
363 *
364 * @module sharedaddy
365 *
366 * @since 11.0
367 *
368 * @param array $data_attributes Attributes supplied from the sharing source.
369 * Note that 'data-' will be prepended to all keys.
370 * @param Sharing_Source $this Sharing source instance.
371 * @param string|false $id Sharing ID.
372 * @param array $args Array of sharing service options.
373 */
374 $data_attributes = apply_filters( 'jetpack_sharing_data_attributes', (array) $data_attributes, $this, $id, $args );
375
376 $id_attr = $id ? esc_attr( $id ) : '';
377 $encoded_data_attributes = '';
378 if ( ! empty( $data_attributes ) ) {
379 // Check for aria-labelledby first, and separate this out.
380 if ( isset( $data_attributes['aria-labelledby'] ) ) {
381 $id_attr = $data_attributes['aria-labelledby'];
382 unset( $data_attributes['aria-labelledby'] );
383 }
384
385 $encoded_data_attributes = implode(
386 ' ',
387 array_map(
388 /** Filter for formatting attributes */
389 function ( $data_key, $data_value ) {
390 return sprintf(
391 'data-%s="%s"',
392 esc_attr( str_replace( array( ' ', '"' ), '', $data_key ) ),
393 esc_attr( $data_value )
394 );
395 },
396 array_keys( $data_attributes ),
397 array_values( $data_attributes )
398 )
399 );
400 }
401
402 $rel_attr = ( true === $this->open_link_in_new ) ? 'noopener noreferrer' : '';
403 $target_attr = ( true === $this->open_link_in_new ) ? 'target="_blank"' : '';
404
405 $classes = implode( ' ', $klasses );
406
407 return sprintf(
408 '<a rel="nofollow %s"
409 data-shared="%s"
410 class="%s"
411 href="%s"
412 %s
413 aria-labelledby="%s"
414 %s>
415 <span id="%s" hidden>%s</span>
416 <span>%s</span>
417 </a>',
418 $rel_attr,
419 $id_attr,
420 esc_attr( $classes ),
421 esc_url( $url ),
422 $target_attr,
423 $id_attr,
424 $encoded_data_attributes,
425 $id_attr,
426 esc_html( $accessible_name ),
427 esc_html( $text )
428 );
429 }
430
431 /**
432 * Get an unfiltered post permalink to use when generating a sharing URL with get_link.
433 * Use instead of get_share_url for non-official styles as get_permalink ensures that process_request
434 * will be executed more reliably, in the case that the filtered URL uses a service that strips query parameters.
435 *
436 * @since 3.7.0
437 * @param int $post_id Post ID.
438 *
439 * @uses get_permalink
440 *
441 * @return string get_permalink( $post_id ) Post permalink.
442 */
443 public function get_process_request_url( $post_id ) {
444 return get_permalink( $post_id );
445 }
446
447 /**
448 * Get sharing name.
449 */
450 abstract public function get_name();
451
452 /**
453 * Get the markup of the sharing button.
454 *
455 * @param WP_Post $post Post object.
456 */
457 abstract public function get_display( $post );
458
459 /**
460 * Add content specific to a service in the head.
461 */
462 public function display_header() {
463 }
464
465 /**
466 * Add content specific to a service in the footer.
467 */
468 public function display_footer() {
469 }
470
471 /**
472 * Does the service have advanced options.
473 *
474 * @return bool
475 */
476 public function has_advanced_options() {
477 return false;
478 }
479
480 /**
481 * Get the AMP specific markup for a sharing button.
482 *
483 * @param \WP_Post $post The current post being viewed.
484 *
485 * @return bool|string
486 */
487 public function get_amp_display( $post ) {
488 // Only display markup if we're on a post.
489 if ( empty( $post ) ) {
490 return false;
491 }
492
493 return $this->build_amp_markup();
494 }
495
496 /**
497 * Generates and returns the markup for an AMP sharing button.
498 *
499 * @param array $attrs Custom attributes for rendering the social icon.
500 *
501 * @return string
502 */
503 protected function build_amp_markup( $attrs = array() ) {
504
505 $title = sprintf(
506 /* translators: placeholder is a service name, such as "Twitter" or "Facebook". */
507 __( 'Click to share on %s', 'jetpack' ),
508 $this->get_name()
509 );
510
511 $attrs = array_merge(
512 array(
513 'type' => $this->get_id(),
514 'height' => '32px',
515 'width' => '32px',
516 'aria-label' => $title,
517 'title' => $title,
518 ),
519 $attrs
520 );
521 $sharing_link = '<amp-social-share';
522 foreach ( $attrs as $key => $value ) {
523 $sharing_link .= sprintf( ' %s="%s"', sanitize_key( $key ), esc_attr( $value ) );
524 }
525 $sharing_link .= '></amp-social-share>';
526 return $sharing_link;
527 }
528
529 /**
530 * Display a preview of the sharing button.
531 *
532 * @param bool $echo Whether to echo the output or return it.
533 * @param bool $force_smart Whether to force the smart (official) services to be shown.
534 * @param null|string $button_style Button style.
535 *
536 * @return string|void
537 */
538 public function display_preview( $echo = true, $force_smart = false, $button_style = null ) {
539 $text = '&nbsp;';
540 $button_style = ( ! empty( $button_style ) ) ? $button_style : $this->button_style;
541 if ( ! $this->smart && ! $force_smart ) {
542 if ( $button_style !== 'icon' ) {
543 $text = $this->get_name();
544 }
545 }
546
547 $klasses = array( 'share-' . $this->get_class(), 'sd-button' );
548
549 if ( $button_style === 'icon' || $button_style === 'icon-text' ) {
550 $klasses[] = 'share-icon';
551 }
552
553 if ( $button_style === 'icon' ) {
554 $klasses[] = 'no-text';
555 }
556
557 if ( $button_style === 'text' ) {
558 $klasses[] = 'no-icon';
559 }
560
561 $is_deprecated = $this->is_deprecated();
562
563 $link = sprintf(
564 '<a rel="nofollow" class="%s" href="javascript:void(0)" title="%s"><span>%s</span></a>',
565 implode( ' ', $klasses ),
566 esc_attr(
567 $is_deprecated
568 /* translators: %1$s is the name of a deprecated Sharing Service like "Google+" */
569 ? sprintf( __( 'The %1$s sharing service has shut down or discontinued support for sharing buttons. This sharing button is not displayed to your visitors and should be removed.', 'jetpack' ), $this->get_name() )
570 : $this->get_name()
571 ),
572 esc_html(
573 $is_deprecated
574 /* translators: %1$s is the name of a deprecated Sharing Service like "Google+" */
575 ? sprintf( __( '%1$s is no longer supported', 'jetpack' ), $this->get_name() )
576 : $text
577 )
578 );
579
580 $smart = ( $this->smart || $force_smart ) ? 'on' : 'off';
581 $return = "<div class='option option-smart-$smart'>$link</div>";
582 if ( $echo ) {
583 echo $return; // phpcs:ignore WordPress.Security.EscapeOutput.OutputNotEscaped -- we escape things above.
584 }
585
586 return $return;
587 }
588
589 /**
590 * Get sharing stats for a specific post or sharing service.
591 *
592 * @param bool|WP_Post $post Post object.
593 *
594 * @return int
595 */
596 public function get_total( $post = false ) {
597 global $wpdb, $blog_id;
598
599 $name = strtolower( $this->get_id() );
600
601 if ( $post === false ) {
602 // get total number of shares for service
603 return (int) $wpdb->get_var( $wpdb->prepare( 'SELECT SUM( count ) FROM sharing_stats WHERE blog_id = %d AND share_service = %s', $blog_id, $name ) );
604 }
605
606 // get total shares for a post
607 return (int) $wpdb->get_var( $wpdb->prepare( 'SELECT count FROM sharing_stats WHERE blog_id = %d AND post_id = %d AND share_service = %s', $blog_id, $post->ID, $name ) );
608 }
609
610 /**
611 * Get sharing stats for all posts on the site.
612 *
613 * @return array
614 */
615 public function get_posts_total() {
616 global $wpdb, $blog_id;
617
618 $totals = array();
619 $name = strtolower( $this->get_id() );
620
621 $my_data = $wpdb->get_results( $wpdb->prepare( 'SELECT post_id as id, SUM( count ) as total FROM sharing_stats WHERE blog_id = %d AND share_service = %s GROUP BY post_id ORDER BY count DESC ', $blog_id, $name ) );
622
623 if ( ! empty( $my_data ) ) {
624 foreach ( $my_data as $row ) {
625 $totals[] = new Sharing_Post_Total( $row->id, $row->total );
626 }
627 }
628
629 usort( $totals, array( 'Sharing_Post_Total', 'cmp' ) );
630
631 return $totals;
632 }
633
634 /**
635 * Process sharing request. Add actions that need to happen when sharing here.
636 *
637 * @param WP_Post $post Post object.
638 * @param array $post_data Array of information about the post we're sharing.
639 *
640 * @return void
641 */
642 public function process_request( $post, array $post_data ) { // phpcs:ignore VariableAnalysis.CodeAnalysis.VariableAnalysis.UnusedVariable
643 /**
644 * Fires when a post is shared via one of the sharing buttons.
645 *
646 * @module sharedaddy
647 *
648 * @since 1.1.0
649 *
650 * @param array $args Aray of information about the sharing service.
651 */
652 do_action(
653 'sharing_bump_stats',
654 array(
655 'service' => $this,
656 'post' => $post,
657 )
658 );
659 }
660
661 /**
662 * Redirect to an external social network site to finish sharing.
663 *
664 * @param string $url Sharing URL for a given service.
665 * @return never
666 */
667 public function redirect_request( $url ) {
668 wp_redirect( $url ); // phpcs:ignore WordPress.Security.SafeRedirect.wp_redirect_wp_redirect -- We allow external redirects here; we define them ourselves.
669
670 // We set up this custom header to indicate to search engines not to index this page.
671 header( 'X-Robots-Tag: noindex, nofollow' );
672 die( 0 );
673 }
674
675 /**
676 * Add extra JavaScript to a sharing service.
677 *
678 * @param string $name Sharing service name.
679 * @param array $params Array of sharing options.
680 *
681 * @return void
682 */
683 public function js_dialog( $name, $params = array() ) {
684 if ( true !== $this->open_link_in_new ) {
685 return;
686 }
687
688 $defaults = array(
689 'menubar' => 1,
690 'resizable' => 1,
691 'width' => 600,
692 'height' => 400,
693 );
694 $params = array_merge( $defaults, $params );
695 $opts = array();
696 foreach ( $params as $key => $val ) {
697 $opts[] = "$key=$val";
698 }
699 $opts = implode( ',', $opts );
700
701 // Add JS after sharing-js has been enqueued.
702 wp_add_inline_script(
703 'sharing-js',
704 "var windowOpen;
705 ( function () {
706 function matches( el, sel ) {
707 return !! (
708 el.matches && el.matches( sel ) ||
709 el.msMatchesSelector && el.msMatchesSelector( sel )
710 );
711 }
712
713 document.body.addEventListener( 'click', function ( event ) {
714 if ( ! event.target ) {
715 return;
716 }
717
718 var el;
719 if ( matches( event.target, 'a.share-$name' ) ) {
720 el = event.target;
721 } else if ( event.target.parentNode && matches( event.target.parentNode, 'a.share-$name' ) ) {
722 el = event.target.parentNode;
723 }
724
725 if ( el ) {
726 event.preventDefault();
727
728 // If there's another sharing window open, close it.
729 if ( typeof windowOpen !== 'undefined' ) {
730 windowOpen.close();
731 }
732 windowOpen = window.open( el.getAttribute( 'href' ), 'wpcom$name', '$opts' );
733 return false;
734 }
735 } );
736 } )();"
737 );
738 }
739 }
740
741 /**
742 * Handle the display of deprecated sharing services.
743 */
744 abstract class Deprecated_Sharing_Source extends Sharing_Source {
745 /**
746 * Button style (icon-text, icon, or text)
747 *
748 * @var string
749 */
750 public $button_style = 'text';
751
752 /**
753 * Does the service have an official version.
754 *
755 * @var bool
756 */
757 public $smart = false;
758
759 /**
760 * Should the sharing link open in a new tab.
761 *
762 * @var bool
763 */
764 protected $open_link_in_new = false;
765
766 /**
767 * Sharing unique ID.
768 *
769 * @var int
770 */
771 protected $id;
772
773 /**
774 * Is the service deprecated.
775 *
776 * @var bool
777 */
778 protected $deprecated = true;
779
780 /**
781 * Constructor.
782 *
783 * @param int $id Sharing source ID.
784 * @param array $settings Sharing settings.
785 */
786 final public function __construct( $id, array $settings ) {
787 $this->id = $id;
788
789 if ( isset( $settings['button_style'] ) ) {
790 $this->button_style = $settings['button_style'];
791 }
792 }
793
794 /**
795 * Is the service deprecated.
796 *
797 * @return bool
798 */
799 final public function is_deprecated() {
800 return true;
801 }
802
803 /**
804 * Get a post's permalink to use for sharing.
805 *
806 * @param int $post_id Post ID.
807 *
808 * @return string
809 */
810 final public function get_share_url( $post_id ) {
811 return get_permalink( $post_id );
812 }
813
814 /**
815 * No AMP display for deprecated sources.
816 *
817 * @param \WP_Post $post The current post being viewed.
818 */
819 final public function get_amp_display( $post ) { // phpcs:ignore VariableAnalysis.CodeAnalysis.VariableAnalysis.UnusedVariable
820 return false;
821 }
822
823 /**
824 * Display a preview of the sharing button.
825 *
826 * @param bool $echo Whether to echo the output or return it.
827 * @param bool $force_smart Whether to force the smart (official) services to be shown.
828 * @param null|string $button_style Button style.
829 *
830 * @return string|void
831 */
832 final public function display_preview( $echo = true, $force_smart = false, $button_style = null ) {
833 return parent::display_preview( $echo, false, $button_style );
834 }
835
836 /**
837 * Get sharing stats for a specific post or sharing service.
838 *
839 * @param bool|WP_Post $post Post object.
840 *
841 * @return int
842 */
843 final public function get_total( $post = false ) { // phpcs:ignore VariableAnalysis.CodeAnalysis.VariableAnalysis.UnusedVariable
844 return 0;
845 }
846
847 /**
848 * Get sharing stats for all posts on the site.
849 *
850 * @return int|array
851 */
852 final public function get_posts_total() {
853 return 0;
854 }
855
856 /**
857 * Process sharing request. Add actions that need to happen when sharing here.
858 *
859 * @param WP_Post $post Post object.
860 * @param array $post_data Array of information about the post we're sharing.
861 *
862 * @return void
863 */
864 final public function process_request( $post, array $post_data ) { // phpcs:ignore Generic.CodeAnalysis.UselessOverridingMethod.Found
865 parent::process_request( $post, $post_data );
866 }
867
868 /**
869 * Get the markup of the sharing button.
870 *
871 * @param WP_Post $post Post object.
872 *
873 * @return string
874 */
875 final public function get_display( $post ) {
876 if ( current_user_can( 'manage_options' ) ) {
877 return $this->display_deprecated( $post );
878 }
879
880 return '';
881 }
882
883 /**
884 * Display a custom message for deprecated services.
885 *
886 * @param WP_Post $post Post object.
887 *
888 * @return string
889 */
890 public function display_deprecated( $post ) {
891 return $this->get_link(
892 $this->get_share_url( $post->ID ),
893 /* translators: %1$s is the name of a deprecated Sharing Service like "Google+" */
894 sprintf( __( '%1$s is no longer supported', 'jetpack' ), $this->get_name() ),
895 /* translators: %1$s is the name of a deprecated Sharing Service like "Google+" */
896 sprintf( __( 'The %1$s sharing service has shut down or discontinued support for sharing buttons. This sharing button is not displayed to your visitors and should be removed.', 'jetpack' ), $this->get_name() )
897 );
898 }
899 }
900
901 /**
902 * Handle the display of advanced sharing services.
903 * Custom sharing buttons we create ourselves will be such services.
904 */
905 abstract class Sharing_Advanced_Source extends Sharing_Source {
906 /**
907 * Does the service have advanced options.
908 *
909 * @return bool
910 */
911 public function has_advanced_options() {
912 return true;
913 }
914
915 /**
916 * Display options for our sharing buttons.
917 */
918 abstract public function display_options();
919
920 /**
921 * Sanitize and save options for our sharing buttons.
922 *
923 * @param array $data Data to be saved.
924 *
925 * @return void
926 */
927 abstract public function update_options( array $data );
928
929 /**
930 * Get array of information about the service.
931 *
932 * @return array
933 */
934 abstract public function get_options();
935 }
936
937 /**
938 * Handle the display of the email sharing button.
939 */
940 class Share_Email extends Sharing_Source {
941 /**
942 * Service short name.
943 *
944 * @var string
945 */
946 public $shortname = 'email';
947
948 /**
949 * Service icon font code.
950 *
951 * @var string
952 */
953 public $icon = '\f410';
954
955 /**
956 * Constructor.
957 *
958 * @param int $id Sharing source ID.
959 * @param array $settings Sharing settings.
960 */
961 public function __construct( $id, array $settings ) {
962 parent::__construct( $id, $settings );
963
964 if ( 'official' === $this->button_style ) {
965 $this->smart = true;
966 } else {
967 $this->smart = false;
968 }
969 }
970
971 /**
972 * Service name.
973 *
974 * @return string
975 */
976 public function get_name() {
977 return _x( 'Email', 'as sharing source', 'jetpack' );
978 }
979
980 /**
981 * Helper function to return a nonce action based on the current post.
982 *
983 * @param WP_Post|null $post The current post if it is defined.
984 * @return string The nonce action name.
985 */
986 protected function get_email_share_nonce_action( $post ) {
987 if ( ! empty( $post ) && $post instanceof WP_Post ) {
988 return 'jetpack-email-share-' . $post->ID;
989 }
990
991 return 'jetpack-email-share';
992 }
993
994 /**
995 * Process sharing request. Add actions that need to happen when sharing here.
996 *
997 * @param WP_Post $post Post object.
998 * @param array $post_data Array of information about the post we're sharing.
999 *
1000 * @return void
1001 */
1002 public function process_request( $post, array $post_data ) {
1003 $is_ajax = false;
1004 if (
1005 isset( $_SERVER['HTTP_X_REQUESTED_WITH'] )
1006 && strtolower( sanitize_text_field( wp_unslash( $_SERVER['HTTP_X_REQUESTED_WITH'] ) ) ) === 'xmlhttprequest'
1007 ) {
1008 $is_ajax = true;
1009 }
1010
1011 // Require an AJAX-driven submit and a valid nonce to process the request
1012 if (
1013 $is_ajax
1014 && isset( $post_data['email-share-nonce'] )
1015 && wp_verify_nonce( $post_data['email-share-nonce'], $this->get_email_share_nonce_action( $post ) )
1016 ) {
1017 // Ensure that we bump stats
1018 parent::process_request( $post, $post_data );
1019 }
1020
1021 if ( $is_ajax ) {
1022 wp_send_json_success();
1023 } else {
1024 wp_safe_redirect( get_permalink( $post->ID ) . '?shared=email&msg=fail' );
1025 exit( 0 );
1026 }
1027
1028 wp_die();
1029 }
1030
1031 /**
1032 * Get the markup of the sharing button.
1033 *
1034 * @param WP_Post $post Post object.
1035 *
1036 * @return string The HTML for the button.
1037 */
1038 public function get_display( $post ) {
1039 $tracking_url = $this->get_process_request_url( $post->ID );
1040 if ( false === stripos( $tracking_url, '?' ) ) {
1041 $tracking_url .= '?';
1042 } else {
1043 $tracking_url .= '&';
1044 }
1045 $tracking_url .= 'share=email';
1046
1047 $data_attributes = array(
1048 'email-share-error-title' => __( 'Do you have email set up?', 'jetpack' ),
1049 'email-share-error-text' => __(
1050 "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.",
1051 'jetpack'
1052 ),
1053 'email-share-nonce' => wp_create_nonce( $this->get_email_share_nonce_action( $post ) ),
1054 'email-share-track-url' => $tracking_url,
1055 'aria-labelledby' => 'sharing-email-' . $post->ID,
1056 );
1057
1058 $post_title = $this->get_share_title( $post->ID );
1059 $post_url = $this->get_share_url( $post->ID );
1060
1061 /** This filter is documented in plugins/jetpack/modules/sharedaddy/sharedaddy.php */
1062 $email_subject = apply_filters(
1063 'wp_sharing_email_send_post_subject',
1064 sprintf( '[%s] %s', __( 'Shared Post', 'jetpack' ), $post_title )
1065 );
1066
1067 $mailto_query = sprintf(
1068 'subject=%s&body=%s&share=email',
1069 rawurlencode( $email_subject ),
1070 rawurlencode( $post_url )
1071 );
1072
1073 return $this->get_link(
1074 'mailto:',
1075 _x( 'Email', 'share to', 'jetpack' ),
1076 __( 'Click to email a link to a friend', 'jetpack' ),
1077 $mailto_query,
1078 false,
1079 $data_attributes
1080 );
1081 }
1082
1083 /**
1084 * AMP display for email.
1085 *
1086 * @param \WP_Post $post The current post being viewed.
1087 */
1088 public function get_amp_display( $post ) { // phpcs:ignore
1089 $attrs = array(
1090 // Prevents an empty window from opening on desktop: https://github.com/ampproject/amphtml/issues/9157.
1091 'data-target' => '_self',
1092 );
1093
1094 return $this->build_amp_markup( $attrs );
1095 }
1096 }
1097
1098 /**
1099 * Legacy Twitter sharing button.
1100 * Share_X is the new Twitter sharing button.
1101 */
1102 class Share_Twitter extends Sharing_Source {
1103 /**
1104 * Service short name.
1105 *
1106 * @var string
1107 */
1108 public $shortname = 'twitter';
1109
1110 /**
1111 * Service icon font code.
1112 *
1113 * @var string
1114 */
1115 public $icon = '\f10e';
1116
1117 /**
1118 * Length of a URL on Twitter.
1119 * 'https://dev.twitter.com/rest/reference/get/help/configuration'
1120 * ( 2015/02/06 ) short_url_length is 22, short_url_length_https is 23
1121 *
1122 * @var int
1123 */
1124 public $short_url_length = 24;
1125
1126 /**
1127 * Constructor.
1128 *
1129 * @param int $id Sharing source ID.
1130 * @param array $settings Sharing settings.
1131 */
1132 public function __construct( $id, array $settings ) {
1133 parent::__construct( $id, $settings );
1134
1135 if ( 'official' === $this->button_style ) {
1136 $this->smart = true;
1137 } else {
1138 $this->smart = false;
1139 }
1140 }
1141
1142 /**
1143 * Service name.
1144 *
1145 * @return string
1146 */
1147 public function get_name() {
1148 return __( 'X', 'jetpack' );
1149 }
1150
1151 /**
1152 * Determine the Twitter 'via' value for a post.
1153 *
1154 * @param WP_Post|int $post Post object or post ID.
1155 * @return string Twitter handle without the preceding @.
1156 **/
1157 public static function sharing_twitter_via( $post ) {
1158 $post = get_post( $post );
1159 /**
1160 * Allow third-party plugins to customize the Twitter username used as "twitter:site" Twitter Card Meta Tag.
1161 *
1162 * @module sharedaddy
1163 *
1164 * @since 3.0.0
1165 *
1166 * @param string $string Twitter Username.
1167 * @param array $args Array of Open Graph Meta Tags and Twitter Cards tags.
1168 */
1169 $twitter_site_tag_value = apply_filters(
1170 'jetpack_twitter_cards_site_tag',
1171 '',
1172 /** This action is documented in modules/sharedaddy/sharing-sources.php */
1173 array( 'twitter:creator' => apply_filters( 'jetpack_sharing_twitter_via', '', $post->ID ) )
1174 );
1175
1176 /*
1177 * Hack to remove the unwanted behavior of adding 'via @jetpack' which
1178 * was introduced with the adding of the Twitter cards.
1179 * This should be a temporary solution until a better method is setup.
1180 */
1181 if ( 'jetpack' === $twitter_site_tag_value ) {
1182 $twitter_site_tag_value = '';
1183 }
1184
1185 /**
1186 * Filters the Twitter username used as "via" in the Twitter sharing button.
1187 *
1188 * @module sharedaddy
1189 *
1190 * @since 1.7.0
1191 *
1192 * @param string $twitter_site_tag_value Twitter Username.
1193 * @param int $post->ID Post ID.
1194 */
1195 $twitter_site_tag_value = apply_filters( 'jetpack_sharing_twitter_via', $twitter_site_tag_value, $post->ID );
1196
1197 // Strip out anything other than a letter, number, or underscore.
1198 // This will prevent the inadvertent inclusion of an extra @, as well as normalizing the handle.
1199 return preg_replace( '/[^\da-z_]+/i', '', $twitter_site_tag_value );
1200 }
1201
1202 /**
1203 * Determine the 'related' Twitter accounts for a post.
1204 *
1205 * @param WP_Post|int $post Post object or post ID.
1206 * @return string Comma-separated list of Twitter handles.
1207 **/
1208 public static function get_related_accounts( $post ) {
1209 $post = get_post( $post );
1210 /**
1211 * Filter the list of related Twitter accounts added to the Twitter sharing button.
1212 *
1213 * @module sharedaddy
1214 *
1215 * @since 1.7.0
1216 *
1217 * @param array $args Array of Twitter usernames. Format is 'username' => 'Optional description'
1218 * @param int $post->ID Post ID.
1219 */
1220 $related_accounts = apply_filters( 'jetpack_sharing_twitter_related', array(), $post->ID );
1221
1222 // Example related string: account1,account2:Account 2 description,account3
1223 $related = array();
1224
1225 foreach ( $related_accounts as $related_account_username => $related_account_description ) {
1226 // Join the description onto the end of the username
1227 if ( $related_account_description ) {
1228 $related_account_username .= ':' . $related_account_description;
1229 }
1230
1231 $related[] = $related_account_username;
1232 }
1233
1234 return implode( ',', $related );
1235 }
1236
1237 /**
1238 * Get the markup of the sharing button.
1239 *
1240 * @param WP_Post $post Post object.
1241 *
1242 * @return string
1243 */
1244 public function get_display( $post ) {
1245 $via = static::sharing_twitter_via( $post );
1246
1247 if ( $via ) {
1248 $via = 'data-via="' . esc_attr( $via ) . '"';
1249 } else {
1250 $via = '';
1251 }
1252
1253 $related = static::get_related_accounts( $post );
1254 if ( ! empty( $related ) && $related !== $via ) {
1255 $related = 'data-related="' . esc_attr( $related ) . '"';
1256 } else {
1257 $related = '';
1258 }
1259
1260 if ( $this->smart ) {
1261 $share_url = $this->get_share_url( $post->ID );
1262 $post_title = $this->get_share_title( $post->ID );
1263 return sprintf(
1264 '<a href="https://twitter.com/share" class="twitter-share-button" data-url="%1$s" data-text="%2$s" %3$s %4$s>Tweet</a>',
1265 esc_url( $share_url ),
1266 esc_attr( $post_title ),
1267 $via,
1268 $related
1269 );
1270 } else {
1271 if (
1272 /**
1273 * Allow plugins to disable sharing counts for specific sharing services.
1274 *
1275 * @module sharedaddy
1276 *
1277 * @since 3.0.0
1278 *
1279 * @param bool true Should sharing counts be enabled for this specific service. Default to true.
1280 * @param int $post->ID Post ID.
1281 * @param string $str Sharing service name.
1282 */
1283 apply_filters( 'jetpack_register_post_for_share_counts', true, $post->ID, 'twitter' )
1284 ) {
1285 sharing_register_post_for_share_counts( $post->ID );
1286 }
1287 return $this->get_link( $this->get_process_request_url( $post->ID ), _x( 'X', 'share to', 'jetpack' ), __( 'Click to share on X', 'jetpack' ), 'share=twitter', 'sharing-twitter-' . $post->ID );
1288 }
1289 }
1290
1291 /**
1292 * Process sharing request. Add actions that need to happen when sharing here.
1293 *
1294 * @param WP_Post $post Post object.
1295 * @param array $post_data Array of information about the post we're sharing.
1296 *
1297 * @return void
1298 */
1299 public function process_request( $post, array $post_data ) {
1300 $post_title = $this->get_share_title( $post->ID );
1301 $post_link = $this->get_share_url( $post->ID );
1302
1303 if ( function_exists( 'mb_stripos' ) ) {
1304 $strlen = 'mb_strlen';
1305 $substr = 'mb_substr';
1306 } else {
1307 $strlen = 'strlen';
1308 $substr = 'substr';
1309 }
1310
1311 $via = static::sharing_twitter_via( $post );
1312 $related = static::get_related_accounts( $post );
1313 if ( $via ) {
1314 $sig = " via @$via";
1315 if ( $related === $via ) {
1316 $related = false;
1317 }
1318 } else {
1319 $via = false;
1320 $sig = '';
1321 }
1322
1323 $suffix_length = $this->short_url_length + $strlen( $sig );
1324 // $sig is handled by twitter in their 'via' argument.
1325 // $post_link is handled by twitter in their 'url' argument.
1326 if ( 280 < $strlen( $post_title ) + $suffix_length ) {
1327 // The -1 is for "\xE2\x80\xA6", a UTF-8 ellipsis.
1328 $text = $substr( $post_title, 0, 280 - $suffix_length - 1 ) . "\xE2\x80\xA6";
1329 } else {
1330 $text = $post_title;
1331 }
1332
1333 // Record stats
1334 parent::process_request( $post, $post_data );
1335
1336 $url = $post_link;
1337 $twitter_url = add_query_arg(
1338 rawurlencode_deep( array_filter( compact( 'via', 'related', 'text', 'url' ) ) ),
1339 'https://x.com/intent/tweet'
1340 );
1341
1342 parent::redirect_request( $twitter_url );
1343 }
1344
1345 /**
1346 * Does this sharing source have a custom style.
1347 *
1348 * @return bool
1349 */
1350 public function has_custom_button_style() {
1351 return $this->smart;
1352 }
1353
1354 /**
1355 * Add content specific to a service in the footer.
1356 */
1357 public function display_footer() {
1358 if ( $this->smart ) {
1359 ?>
1360 <script>!function(d,s,id){var js,fjs=d.getElementsByTagName(s)[0],p=/^http:/.test(d.location)?'http':'https';if(!d.getElementById(id)){js=d.createElement(s);js.id=id;js.src=p+'://platform.twitter.com/widgets.js';fjs.parentNode.insertBefore(js,fjs);}}(document, 'script', 'twitter-wjs');</script>
1361 <?php
1362 } else {
1363 $this->js_dialog( $this->shortname, array( 'height' => 350 ) );
1364 }
1365 }
1366 }
1367
1368 /**
1369 * X sharing button.
1370 *
1371 * While the old Twitter button had an official button,
1372 * this new X button does not, since there is no official X button yet.
1373 */
1374 class Share_X extends Sharing_Source {
1375 /**
1376 * Service short name.
1377 *
1378 * @var string
1379 */
1380 public $shortname = 'x';
1381
1382 /**
1383 * Service icon font code.
1384 *
1385 * @var string
1386 */
1387 public $icon = '\f10e';
1388
1389 /**
1390 * Length of a URL on X.
1391 * https://developer.twitter.com/en/docs/tco
1392 *
1393 * @var int
1394 */
1395 public $short_url_length = 24;
1396
1397 /**
1398 * Constructor.
1399 *
1400 * @param int $id Sharing source ID.
1401 * @param array $settings Sharing settings.
1402 */
1403 public function __construct( $id, array $settings ) {
1404 parent::__construct( $id, $settings );
1405
1406 if ( 'official' === $this->button_style ) {
1407 $this->smart = true;
1408 } else {
1409 $this->smart = false;
1410 }
1411 }
1412
1413 /**
1414 * Service name.
1415 *
1416 * @return string
1417 */
1418 public function get_name() {
1419 return __( 'X', 'jetpack' );
1420 }
1421
1422 /**
1423 * Get the markup of the sharing button.
1424 *
1425 * @param WP_Post $post Post object.
1426 *
1427 * @return string
1428 */
1429 public function get_display( $post ) {
1430 $via = static::sharing_x_via( $post );
1431
1432 if ( $via ) {
1433 $via = 'data-via="' . esc_attr( $via ) . '"';
1434 } else {
1435 $via = '';
1436 }
1437
1438 $related = static::get_related_accounts( $post );
1439 if ( ! empty( $related ) && $related !== $via ) {
1440 $related = 'data-related="' . esc_attr( $related ) . '"';
1441 } else {
1442 $related = '';
1443 }
1444
1445 if ( $this->smart ) {
1446 $share_url = $this->get_share_url( $post->ID );
1447 $post_title = $this->get_share_title( $post->ID );
1448 return sprintf(
1449 '<a href="https://x.com/share" class="twitter-share-button" data-url="%1$s" data-text="%2$s" %3$s %4$s>%5$s</a>',
1450 esc_url( $share_url ),
1451 esc_attr( $post_title ),
1452 $via,
1453 $related,
1454 esc_html__( 'Post', 'jetpack' )
1455 );
1456 } else {
1457 if (
1458 /** This filter is documented in modules/sharedaddy/sharing-sources.php */
1459 apply_filters( 'jetpack_register_post_for_share_counts', true, $post->ID, 'x' )
1460 ) {
1461 sharing_register_post_for_share_counts( $post->ID );
1462 }
1463 return $this->get_link(
1464 $this->get_process_request_url( $post->ID ),
1465 _x( 'X', 'share to', 'jetpack' ),
1466 __( 'Click to share on X', 'jetpack' ),
1467 'share=x',
1468 'sharing-x-' . $post->ID
1469 );
1470 }
1471 }
1472
1473 /**
1474 * Determine the X 'via' value for a post.
1475 *
1476 * @param WP_Post|int $post Post object or post ID.
1477 * @return string X handle without the preceding @.
1478 **/
1479 public static function sharing_x_via( $post ) {
1480 $post = get_post( $post );
1481 /** This filter is documented in modules/sharedaddy/sharing-sources.php */
1482 $twitter_site_tag_value = apply_filters(
1483 'jetpack_twitter_cards_site_tag',
1484 '',
1485 /** This action is documented in modules/sharedaddy/sharing-sources.php */
1486 array( 'twitter:creator' => apply_filters( 'jetpack_sharing_twitter_via', '', $post->ID ) )
1487 );
1488
1489 /*
1490 * Hack to remove the unwanted behavior of adding 'via @jetpack' which
1491 * was introduced with the adding of the Twitter cards.
1492 * This should be a temporary solution until a better method is setup.
1493 */
1494 if ( 'jetpack' === $twitter_site_tag_value ) {
1495 $twitter_site_tag_value = '';
1496 }
1497
1498 /** This filter is documented in modules/sharedaddy/sharing-sources.php */
1499 $twitter_site_tag_value = apply_filters( 'jetpack_sharing_twitter_via', $twitter_site_tag_value, $post->ID );
1500
1501 // Strip out anything other than a letter, number, or underscore.
1502 // This will prevent the inadvertent inclusion of an extra @, as well as normalizing the handle.
1503 return preg_replace( '/[^\da-z_]+/i', '', $twitter_site_tag_value );
1504 }
1505
1506 /**
1507 * Determine the 'related' X accounts for a post.
1508 *
1509 * @param WP_Post|int $post Post object or post ID.
1510 * @return string Comma-separated list of X handles.
1511 **/
1512 public static function get_related_accounts( $post ) {
1513 $post = get_post( $post );
1514 /** This filter is documented in modules/sharedaddy/sharing-sources.php */
1515 $related_accounts = apply_filters( 'jetpack_sharing_twitter_related', array(), $post->ID );
1516
1517 // Example related string: account1,account2:Account 2 description,account3
1518 $related = array();
1519
1520 foreach ( $related_accounts as $related_account_username => $related_account_description ) {
1521 // Join the description onto the end of the username
1522 if ( $related_account_description ) {
1523 $related_account_username .= ':' . $related_account_description;
1524 }
1525
1526 $related[] = $related_account_username;
1527 }
1528
1529 return implode( ',', $related );
1530 }
1531
1532 /**
1533 * Add content specific to a service in the footer.
1534 */
1535 public function display_footer() {
1536 if ( $this->smart ) {
1537 ?>
1538 <script>!function(d,s,id){var js,fjs=d.getElementsByTagName(s)[0],p=/^http:/.test(d.location)?'http':'https';if(!d.getElementById(id)){js=d.createElement(s);js.id=id;js.src=p+'://platform.twitter.com/widgets.js';fjs.parentNode.insertBefore(js,fjs);}}(document, 'script', 'twitter-wjs');</script>
1539 <?php
1540 } else {
1541 $this->js_dialog( $this->shortname, array( 'height' => 350 ) );
1542 }
1543 }
1544
1545 /**
1546 * Process sharing request. Add actions that need to happen when sharing here.
1547 *
1548 * @param WP_Post $post Post object.
1549 * @param array $post_data Array of information about the post we're sharing.
1550 *
1551 * @return void
1552 */
1553 public function process_request( $post, array $post_data ) {
1554 $post_title = $this->get_share_title( $post->ID );
1555 $post_link = $this->get_share_url( $post->ID );
1556
1557 if ( function_exists( 'mb_stripos' ) ) {
1558 $strlen = 'mb_strlen';
1559 $substr = 'mb_substr';
1560 } else {
1561 $strlen = 'strlen';
1562 $substr = 'substr';
1563 }
1564
1565 $via = static::sharing_x_via( $post );
1566 $related = static::get_related_accounts( $post );
1567 if ( $via ) {
1568 $sig = " via @$via";
1569 if ( $related === $via ) {
1570 $related = false;
1571 }
1572 } else {
1573 $via = false;
1574 $sig = '';
1575 }
1576
1577 $suffix_length = $this->short_url_length + $strlen( $sig );
1578 // $sig is handled by twitter in their 'via' argument.
1579 // $post_link is handled by twitter in their 'url' argument.
1580 if ( 280 < $strlen( $post_title ) + $suffix_length ) {
1581 // The -1 is for "\xE2\x80\xA6", a UTF-8 ellipsis.
1582 $text = $substr( $post_title, 0, 280 - $suffix_length - 1 ) . "\xE2\x80\xA6";
1583 } else {
1584 $text = $post_title;
1585 }
1586
1587 // Record stats
1588 parent::process_request( $post, $post_data );
1589
1590 $url = $post_link;
1591 $twitter_url = add_query_arg(
1592 rawurlencode_deep( array_filter( compact( 'via', 'related', 'text', 'url' ) ) ),
1593 'https://x.com/intent/tweet'
1594 );
1595
1596 parent::redirect_request( $twitter_url );
1597 }
1598
1599 /**
1600 * Does this sharing source have a custom style.
1601 *
1602 * @return bool
1603 */
1604 public function has_custom_button_style() {
1605 return $this->smart;
1606 }
1607 }
1608
1609 /**
1610 * Reddit sharing button.
1611 */
1612 class Share_Reddit extends Sharing_Source {
1613 /**
1614 * Service short name.
1615 *
1616 * @var string
1617 */
1618 public $shortname = 'reddit';
1619
1620 /**
1621 * Service icon font code.
1622 *
1623 * @var string
1624 */
1625 public $icon = '\f222';
1626
1627 /**
1628 * Service name.
1629 *
1630 * @return string
1631 */
1632 public function get_name() {
1633 return __( 'Reddit', 'jetpack' );
1634 }
1635
1636 /**
1637 * Get the markup of the sharing button.
1638 *
1639 * @param WP_Post $post Post object.
1640 *
1641 * @return string
1642 */
1643 public function get_display( $post ) {
1644 return $this->get_link(
1645 $this->get_process_request_url( $post->ID ),
1646 _x( 'Reddit', 'share to', 'jetpack' ),
1647 __( 'Click to share on Reddit', 'jetpack' ),
1648 'share=reddit',
1649 'sharing-reddit-' . $post->ID
1650 );
1651 }
1652
1653 /**
1654 * AMP display for Reddit.
1655 *
1656 * @param \WP_Post $post The current post being viewed.
1657 */
1658 public function get_amp_display( $post ) {
1659 $attrs = array(
1660 'data-share-endpoint' => esc_url_raw( 'https://reddit.com/submit?url=' . rawurlencode( $this->get_share_url( $post->ID ) ) . '&title=' . rawurlencode( $this->get_share_title( $post->ID ) ) ),
1661 );
1662
1663 return $this->build_amp_markup( $attrs );
1664 }
1665
1666 /**
1667 * Process sharing request. Add actions that need to happen when sharing here.
1668 *
1669 * @param WP_Post $post Post object.
1670 * @param array $post_data Array of information about the post we're sharing.
1671 *
1672 * @return void
1673 */
1674 public function process_request( $post, array $post_data ) {
1675 $reddit_url = $this->http() . '://reddit.com/submit?url=' . rawurlencode( $this->get_share_url( $post->ID ) ) . '&title=' . rawurlencode( $this->get_share_title( $post->ID ) );
1676
1677 // Record stats
1678 parent::process_request( $post, $post_data );
1679
1680 parent::redirect_request( $reddit_url );
1681 }
1682 }
1683
1684 /**
1685 * LinkedIn sharing button.
1686 */
1687 class Share_LinkedIn extends Sharing_Source {
1688 /**
1689 * Service short name.
1690 *
1691 * @var string
1692 */
1693 public $shortname = 'linkedin';
1694
1695 /**
1696 * Service icon font code.
1697 *
1698 * @var string
1699 */
1700 public $icon = '\f207';
1701
1702 /**
1703 * Constructor.
1704 *
1705 * @param int $id Sharing source ID.
1706 * @param array $settings Sharing settings.
1707 */
1708 public function __construct( $id, array $settings ) {
1709 parent::__construct( $id, $settings );
1710
1711 if ( 'official' === $this->button_style ) {
1712 $this->smart = true;
1713 } else {
1714 $this->smart = false;
1715 }
1716 }
1717
1718 /**
1719 * Service name.
1720 *
1721 * @return string
1722 */
1723 public function get_name() {
1724 return __( 'LinkedIn', 'jetpack' );
1725 }
1726
1727 /**
1728 * Does this sharing source have a custom style.
1729 *
1730 * @return bool
1731 */
1732 public function has_custom_button_style() {
1733 return $this->smart;
1734 }
1735
1736 /**
1737 * Get the markup of the sharing button.
1738 *
1739 * @param WP_Post $post Post object.
1740 *
1741 * @return string
1742 */
1743 public function get_display( $post ) {
1744 $display = '';
1745
1746 if ( $this->smart ) {
1747 $share_url = $this->get_share_url( $post->ID );
1748 $display .= sprintf( '<div class="linkedin_button"><script type="in/share" data-url="%s" data-counter="right"></script></div>', esc_url( $share_url ) );
1749 } else {
1750 $display = $this->get_link( $this->get_process_request_url( $post->ID ), _x( 'LinkedIn', 'share to', 'jetpack' ), __( 'Click to share on LinkedIn', 'jetpack' ), 'share=linkedin', 'sharing-linkedin-' . $post->ID );
1751 }
1752
1753 /** This filter is already documented in modules/sharedaddy/sharing-sources.php */
1754 if ( apply_filters( 'jetpack_register_post_for_share_counts', true, $post->ID, 'linkedin' ) ) {
1755 sharing_register_post_for_share_counts( $post->ID );
1756 }
1757
1758 return $display;
1759 }
1760
1761 /**
1762 * Process sharing request. Add actions that need to happen when sharing here.
1763 *
1764 * @param WP_Post $post Post object.
1765 * @param array $post_data Array of information about the post we're sharing.
1766 *
1767 * @return void
1768 */
1769 public function process_request( $post, array $post_data ) {
1770
1771 $post_link = $this->get_share_url( $post->ID );
1772
1773 // Using the same URL as the official button, which is *not* LinkedIn's documented sharing link
1774 // https://www.linkedin.com/cws/share?url={url}&token=&isFramed=false
1775 $linkedin_url = add_query_arg(
1776 array(
1777 'url' => rawurlencode( $post_link ),
1778 ),
1779 'https://www.linkedin.com/cws/share?token=&isFramed=false'
1780 );
1781
1782 // Record stats
1783 parent::process_request( $post, $post_data );
1784
1785 parent::redirect_request( $linkedin_url );
1786 }
1787
1788 /**
1789 * Add content specific to a service in the footer.
1790 */
1791 public function display_footer() {
1792 if ( ! $this->smart ) {
1793 $this->js_dialog(
1794 $this->shortname,
1795 array(
1796 'width' => 580,
1797 'height' => 450,
1798 )
1799 );
1800 } else {
1801 ?>
1802 <script type="text/javascript">
1803 ( function () {
1804 var currentScript = document.currentScript;
1805
1806 // Helper function to load an external script.
1807 function loadScript( url, cb ) {
1808 var script = document.createElement( 'script' );
1809 var prev = currentScript || document.getElementsByTagName( 'script' )[ 0 ];
1810 script.setAttribute( 'async', true );
1811 script.setAttribute( 'src', url );
1812 prev.parentNode.insertBefore( script, prev );
1813 script.addEventListener( 'load', cb );
1814 }
1815
1816 function init() {
1817 loadScript( 'https://platform.linkedin.com/in.js?async=true', function () {
1818 if ( typeof IN !== 'undefined' ) {
1819 IN.init();
1820 }
1821 } );
1822 }
1823
1824 if ( document.readyState === 'loading' ) {
1825 document.addEventListener( 'DOMContentLoaded', init );
1826 } else {
1827 init();
1828 }
1829
1830 document.body.addEventListener( 'is.post-load', function() {
1831 if ( typeof IN !== 'undefined' ) {
1832 IN.parse();
1833 }
1834 } );
1835 } )();
1836 </script>
1837 <?php
1838 }
1839 }
1840 }
1841
1842 /**
1843 * Facebook sharing button.
1844 */
1845 class Share_Facebook extends Sharing_Source {
1846 /**
1847 * Service short name.
1848 *
1849 * @var string
1850 */
1851 public $shortname = 'facebook';
1852
1853 /**
1854 * Service icon font code.
1855 *
1856 * @var string
1857 */
1858 public $icon = '\f204';
1859
1860 /**
1861 * Sharing type.
1862 *
1863 * @var string
1864 */
1865 private $share_type = 'default';
1866
1867 /**
1868 * Constructor.
1869 *
1870 * @param int $id Sharing source ID.
1871 * @param array $settings Sharing settings.
1872 */
1873 public function __construct( $id, array $settings ) {
1874 parent::__construct( $id, $settings );
1875
1876 if ( isset( $settings['share_type'] ) ) {
1877 $this->share_type = $settings['share_type'];
1878 }
1879
1880 if ( 'official' === $this->button_style ) {
1881 $this->smart = true;
1882 } else {
1883 $this->smart = false;
1884 }
1885 }
1886
1887 /**
1888 * Service name.
1889 *
1890 * @return string
1891 */
1892 public function get_name() {
1893 return __( 'Facebook', 'jetpack' );
1894 }
1895
1896 /**
1897 * Add content specific to a service in the head.
1898 */
1899 public function display_header() {
1900 }
1901
1902 /**
1903 * Guess locale from language code.
1904 *
1905 * @param string $lang Language code.
1906 *
1907 * @return string|bool
1908 */
1909 public function guess_locale_from_lang( $lang ) {
1910 if ( 'en' === $lang || 'en_US' === $lang || ! $lang ) {
1911 return 'en_US';
1912 }
1913
1914 if ( ! class_exists( 'GP_Locales' ) ) {
1915 if ( ! defined( 'JETPACK__GLOTPRESS_LOCALES_PATH' ) || ! file_exists( JETPACK__GLOTPRESS_LOCALES_PATH ) ) {
1916 return false;
1917 }
1918
1919 require JETPACK__GLOTPRESS_LOCALES_PATH;
1920 }
1921
1922 if ( defined( 'IS_WPCOM' ) && IS_WPCOM ) {
1923 // WP.com: get_locale() returns 'it'
1924 $locale = GP_Locales::by_slug( $lang );
1925 } else {
1926 // Jetpack: get_locale() returns 'it_IT';
1927 $locale = GP_Locales::by_field( 'wp_locale', $lang );
1928 }
1929
1930 if ( ! $locale ) {
1931 return false;
1932 }
1933
1934 if ( empty( $locale->facebook_locale ) ) {
1935 if ( empty( $locale->wp_locale ) ) {
1936 return false;
1937 } else {
1938 // Facebook SDK is smart enough to fall back to en_US if a
1939 // locale isn't supported. Since supported Facebook locales
1940 // can fall out of sync, we'll attempt to use the known
1941 // wp_locale value and rely on said fallback.
1942 return $locale->wp_locale;
1943 }
1944 }
1945
1946 return $locale->facebook_locale;
1947 }
1948
1949 /**
1950 * Get the markup of the sharing button.
1951 *
1952 * @param WP_Post $post Post object.
1953 *
1954 * @return string
1955 */
1956 public function get_display( $post ) {
1957 if ( $this->smart ) {
1958 $share_url = $this->get_share_url( $post->ID );
1959 $fb_share_html = '<div class="fb-share-button" data-href="' . esc_attr( $share_url ) . '" data-layout="button_count"></div>';
1960 /**
1961 * Filter the output of the Facebook Sharing button.
1962 *
1963 * @module sharedaddy
1964 *
1965 * @since 3.6.0
1966 *
1967 * @param string $fb_share_html Facebook Sharing button HTML.
1968 * @param string $share_url URL of the post to share.
1969 */
1970 return apply_filters( 'jetpack_sharing_facebook_official_button_output', $fb_share_html, $share_url );
1971 }
1972
1973 /** This filter is already documented in modules/sharedaddy/sharing-sources.php */
1974 if ( apply_filters( 'jetpack_register_post_for_share_counts', true, $post->ID, 'facebook' ) ) {
1975 sharing_register_post_for_share_counts( $post->ID );
1976 }
1977 return $this->get_link( $this->get_process_request_url( $post->ID ), _x( 'Facebook', 'share to', 'jetpack' ), __( 'Click to share on Facebook', 'jetpack' ), 'share=facebook', 'sharing-facebook-' . $post->ID );
1978 }
1979
1980 /**
1981 * AMP display for Facebook.
1982 *
1983 * @param \WP_Post $post The current post being viewed.
1984 */
1985 public function get_amp_display( $post ) { // phpcs:ignore VariableAnalysis.CodeAnalysis.VariableAnalysis.UnusedVariable
1986 $attrs = array(
1987 /** This filter is documented in modules/sharedaddy/sharing-sources.php */
1988 'data-param-app_id' => apply_filters( 'jetpack_sharing_facebook_app_id', '249643311490' ),
1989 );
1990
1991 return $this->build_amp_markup( $attrs );
1992 }
1993
1994 /**
1995 * Process sharing request. Add actions that need to happen when sharing here.
1996 *
1997 * @param WP_Post $post Post object.
1998 * @param array $post_data Array of information about the post we're sharing.
1999 *
2000 * @return void
2001 */
2002 public function process_request( $post, array $post_data ) {
2003 $fb_url = $this->http() . '://www.facebook.com/sharer.php?u=' . rawurlencode( $this->get_share_url( $post->ID ) ) . '&t=' . rawurlencode( $this->get_share_title( $post->ID ) );
2004
2005 // Record stats
2006 parent::process_request( $post, $post_data );
2007
2008 parent::redirect_request( $fb_url );
2009 }
2010
2011 /**
2012 * Add content specific to a service in the footer.
2013 */
2014 public function display_footer() {
2015 $this->js_dialog( $this->shortname );
2016 if ( $this->smart ) {
2017 $locale = $this->guess_locale_from_lang( get_locale() );
2018 if ( ! $locale ) {
2019 $locale = 'en_US';
2020 }
2021 /**
2022 * Filter the App ID used in the official Facebook Share button.
2023 *
2024 * @since 3.8.0
2025 *
2026 * @param int $fb_app_id Facebook App ID. Default to 249643311490 (WordPress.com's App ID).
2027 */
2028 $fb_app_id = apply_filters( 'jetpack_sharing_facebook_app_id', '249643311490' );
2029 if ( is_numeric( $fb_app_id ) ) {
2030 $fb_app_id = '&appId=' . $fb_app_id;
2031 } else {
2032 $fb_app_id = '';
2033 }
2034 ?>
2035 <div id="fb-root"></div>
2036 <script>(function(d, s, id) { var js, fjs = d.getElementsByTagName(s)[0]; if (d.getElementById(id)) return; js = d.createElement(s); js.id = id; js.src = 'https://connect.facebook.net/<?php echo esc_attr( $locale ); ?>/sdk.js#xfbml=1<?php echo esc_attr( $fb_app_id ); ?>&version=v2.3'; fjs.parentNode.insertBefore(js, fjs); }(document, 'script', 'facebook-jssdk'));</script>
2037 <script>
2038 document.body.addEventListener( 'is.post-load', function() {
2039 if ( 'undefined' !== typeof FB ) {
2040 FB.XFBML.parse();
2041 }
2042 } );
2043 </script>
2044 <?php
2045 }
2046 }
2047 }
2048
2049 /**
2050 * Print button.
2051 */
2052 class Share_Print extends Sharing_Source {
2053 /**
2054 * Service short name.
2055 *
2056 * @var string
2057 */
2058 public $shortname = 'print';
2059
2060 /**
2061 * Service icon font code.
2062 *
2063 * @var string
2064 */
2065 public $icon = '\f469';
2066
2067 /**
2068 * Constructor.
2069 *
2070 * @param int $id Sharing source ID.
2071 * @param array $settings Sharing settings.
2072 */
2073 public function __construct( $id, array $settings ) {
2074 parent::__construct( $id, $settings );
2075
2076 if ( 'official' === $this->button_style ) {
2077 $this->smart = true;
2078 } else {
2079 $this->smart = false;
2080 }
2081 }
2082
2083 /**
2084 * Service name.
2085 *
2086 * @return string
2087 */
2088 public function get_name() {
2089 return __( 'Print', 'jetpack' );
2090 }
2091
2092 /**
2093 * Get the markup of the sharing button.
2094 *
2095 * @param WP_Post $post Post object.
2096 *
2097 * @return string
2098 */
2099 public function get_display( $post ) {
2100 return $this->get_link( $this->get_process_request_url( $post->ID ) . ( ( is_single() || is_page() ) ? '#print' : '' ), _x( 'Print', 'share to', 'jetpack' ), __( 'Click to print', 'jetpack' ), 'share=print', 'sharing-print-' . $post->ID );
2101 }
2102
2103 /**
2104 * AMP display for Print.
2105 *
2106 * @param \WP_Post $post The current post being viewed.
2107 */
2108 public function get_amp_display( $post ) {
2109 if ( empty( $post ) ) {
2110 return false;
2111 }
2112
2113 return '<button class="amp-social-share print" on="tap:AMP.print">Print</button>';
2114 }
2115 }
2116
2117 /**
2118 * Press This Button.
2119 */
2120 class Share_PressThis extends Sharing_Source {
2121 /**
2122 * Service short name.
2123 *
2124 * @var string
2125 */
2126 public $shortname = 'pressthis';
2127
2128 /**
2129 * Service icon font code.
2130 *
2131 * @var string
2132 */
2133 public $icon = '\f205';
2134
2135 /**
2136 * Constructor.
2137 *
2138 * @param int $id Sharing source ID.
2139 * @param array $settings Sharing settings.
2140 */
2141 public function __construct( $id, array $settings ) {
2142 parent::__construct( $id, $settings );
2143
2144 if ( 'official' === $this->button_style ) {
2145 $this->smart = true;
2146 } else {
2147 $this->smart = false;
2148 }
2149 }
2150
2151 /**
2152 * Service name.
2153 *
2154 * @return string
2155 */
2156 public function get_name() {
2157 return __( 'Press This', 'jetpack' );
2158 }
2159
2160 /**
2161 * Process sharing request. Add actions that need to happen when sharing here.
2162 *
2163 * @param WP_Post $post Post object.
2164 * @param array $post_data Array of information about the post we're sharing.
2165 *
2166 * @return void
2167 */
2168 public function process_request( $post, array $post_data ) {
2169 global $current_user;
2170
2171 $primary_blog = (int) get_user_meta( $current_user->ID, 'primary_blog', true );
2172 if ( $primary_blog ) {
2173 $primary_blog_details = get_blog_details( $primary_blog );
2174 } else {
2175 $primary_blog_details = false;
2176 }
2177
2178 if ( $primary_blog_details ) {
2179 $blogs = array( $primary_blog_details );
2180 } elseif ( function_exists( 'get_active_blogs_for_user' ) ) {
2181 $blogs = get_active_blogs_for_user();
2182 if ( empty( $blogs ) ) {
2183 $blogs = get_blogs_of_user( $current_user->ID );
2184 }
2185 } else {
2186 $blogs = get_blogs_of_user( $current_user->ID );
2187 }
2188
2189 if ( empty( $blogs ) ) {
2190 wp_safe_redirect( get_permalink( $post->ID ) );
2191 die( 0 );
2192 }
2193
2194 $blog = current( $blogs );
2195
2196 $args = array(
2197 'u' => rawurlencode( $this->get_share_url( $post->ID ) ),
2198 );
2199
2200 $args['url-scan-submit'] = 'Scan';
2201 $args['_wpnonce'] = wp_create_nonce( 'scan-site' );
2202
2203 $url = $blog->siteurl . '/wp-admin/press-this.php';
2204 $url = add_query_arg( $args, $url );
2205
2206 // Record stats
2207 parent::process_request( $post, $post_data );
2208
2209 parent::redirect_request( $url );
2210 }
2211
2212 /**
2213 * Get the markup of the sharing button.
2214 *
2215 * @param WP_Post $post Post object.
2216 *
2217 * @return string
2218 */
2219 public function get_display( $post ) {
2220 return $this->get_link( $this->get_process_request_url( $post->ID ), _x( 'Press This', 'share to', 'jetpack' ), __( 'Click to Press This!', 'jetpack' ), 'share=press-this', 'sharing-press-this-' . $post->ID );
2221 }
2222
2223 /**
2224 * No AMP display for PressThis.
2225 *
2226 * @param \WP_Post $post The current post being viewed.
2227 */
2228 public function get_amp_display( $post ) { // phpcs:ignore
2229 return false;
2230 }
2231 }
2232
2233 /**
2234 * Custom (user-defined) sharing button.
2235 */
2236 class Share_Custom extends Sharing_Advanced_Source {
2237 /**
2238 * Sharing service name.
2239 *
2240 * @var string
2241 */
2242 private $name;
2243
2244 /**
2245 * Sharing icon.
2246 *
2247 * @var string
2248 */
2249 private $icon;
2250
2251 /**
2252 * Sharing service URL.
2253 *
2254 * @var string
2255 */
2256 private $url;
2257
2258 /**
2259 * Does the service have an official version.
2260 *
2261 * @var bool
2262 */
2263 public $smart = true;
2264
2265 /**
2266 * Service short name.
2267 *
2268 * @var string
2269 */
2270 public $shortname;
2271
2272 /**
2273 * Custom sharing class.
2274 *
2275 * @return string
2276 */
2277 public function get_class() {
2278 return 'custom share-custom-' . sanitize_html_class( strtolower( $this->name ) );
2279 }
2280
2281 /**
2282 * Constructor.
2283 *
2284 * @param int $id Sharing source ID.
2285 * @param array $settings Sharing settings.
2286 */
2287 public function __construct( $id, array $settings ) {
2288 parent::__construct( $id, $settings );
2289
2290 if ( isset( $settings['name'] ) ) {
2291 $this->name = $settings['name'];
2292 $this->shortname = preg_replace( '/[^a-z0-9]*/', '', $settings['name'] );
2293 }
2294
2295 if ( isset( $settings['icon'] ) ) {
2296 $this->icon = $settings['icon'];
2297
2298 $new_icon = esc_url_raw( wp_specialchars_decode( $this->icon, ENT_QUOTES ) );
2299 $i = 0;
2300 while ( $new_icon !== $this->icon ) {
2301 if ( $i > 5 ) {
2302 $this->icon = false;
2303 break;
2304 } else {
2305 $this->icon = $new_icon;
2306 $new_icon = esc_url_raw( wp_specialchars_decode( $this->icon, ENT_QUOTES ) );
2307 }
2308 ++$i;
2309 }
2310 }
2311
2312 if ( isset( $settings['url'] ) ) {
2313 $this->url = $settings['url'];
2314 }
2315 }
2316
2317 /**
2318 * Service name.
2319 *
2320 * @return string
2321 */
2322 public function get_name() {
2323 return $this->name;
2324 }
2325
2326 /**
2327 * Get the markup of the sharing button.
2328 *
2329 * @param WP_Post $post Post object.
2330 *
2331 * @return string
2332 */
2333 public function get_display( $post ) {
2334 $str = $this->get_link(
2335 $this->get_process_request_url( $post->ID ),
2336 esc_html( $this->name ),
2337 sprintf(
2338 /* Translators: placeholder is the name of a social network. */
2339 __( 'Click to share on %s', 'jetpack' ),
2340 esc_attr( $this->name )
2341 ),
2342 'share=' . $this->id,
2343 'sharing-custom-' . $post->ID
2344 );
2345
2346 $style = 'background-image:url("' . addcslashes( esc_url_raw( $this->icon ), '"' ) . '");';
2347 $class = ( 'icon' === $this->button_style ) ? ' class="custom-sharing-span"' : '';
2348 return str_replace( '<span>', '<span' . $class . ' style="' . esc_attr( $style ) . '">', $str );
2349 }
2350
2351 /**
2352 * No AMP display for custom elements.
2353 *
2354 * @param \WP_Post $post The current post being viewed.
2355 */
2356 public function get_amp_display( $post ) { // phpcs:ignore VariableAnalysis.CodeAnalysis.VariableAnalysis.UnusedVariable
2357 return false;
2358 }
2359
2360 /**
2361 * Process sharing request. Add actions that need to happen when sharing here.
2362 *
2363 * @param WP_Post $post Post object.
2364 * @param array $post_data Array of information about the post we're sharing.
2365 *
2366 * @return void
2367 */
2368 public function process_request( $post, array $post_data ) {
2369 $url = str_replace( '&amp;', '&', $this->url );
2370 $url = str_replace( '%post_id%', rawurlencode( $post->ID ), $url );
2371 $url = str_replace( '%post_url%', rawurlencode( $this->get_share_url( $post->ID ) ), $url );
2372 $url = str_replace( '%post_full_url%', rawurlencode( get_permalink( $post->ID ) ), $url );
2373 $url = str_replace( '%post_title%', rawurlencode( $this->get_share_title( $post->ID ) ), $url );
2374 $url = str_replace( '%home_url%', rawurlencode( home_url() ), $url );
2375 $url = str_replace( '%post_slug%', rawurlencode( $post->post_name ), $url );
2376
2377 if ( strpos( $url, '%post_tags%' ) !== false ) {
2378 $tags = get_the_tags( $post->ID );
2379 $tagged = '';
2380
2381 if ( $tags ) {
2382 $tagged_raw = array();
2383 foreach ( $tags as $tag ) {
2384 $tagged_raw[] = rawurlencode( $tag->name );
2385 }
2386
2387 $tagged = implode( ',', $tagged_raw );
2388 }
2389
2390 $url = str_replace( '%post_tags%', $tagged, $url );
2391 }
2392
2393 if ( strpos( $url, '%post_excerpt%' ) !== false ) {
2394 $url_excerpt = $post->post_excerpt;
2395 if ( empty( $url_excerpt ) ) {
2396 $url_excerpt = $post->post_content;
2397 }
2398
2399 $url_excerpt = wp_strip_all_tags( strip_shortcodes( $url_excerpt ) );
2400 $url_excerpt = wp_html_excerpt( $url_excerpt, 100 );
2401 $url_excerpt = rtrim( preg_replace( '/[^ .]*$/', '', $url_excerpt ) );
2402 $url = str_replace( '%post_excerpt%', rawurlencode( $url_excerpt ), $url );
2403 }
2404
2405 // Record stats
2406 parent::process_request( $post, $post_data );
2407
2408 parent::redirect_request( $url );
2409 }
2410
2411 /**
2412 * Display options for our sharing buttons.
2413 *
2414 * @return void
2415 */
2416 public function display_options() {
2417 ?>
2418 <div class="input">
2419 <table class="form-table">
2420 <tbody>
2421 <tr>
2422 <th scope="row"><?php esc_html_e( 'Label', 'jetpack' ); ?></th>
2423 <td><input type="text" name="name" value="<?php echo esc_attr( $this->name ); ?>" /></td>
2424 </tr>
2425
2426 <tr>
2427 <th scope="row"><?php esc_html_e( 'URL', 'jetpack' ); ?></th>
2428 <td><input type="text" name="url" value="<?php echo esc_attr( $this->url ); ?>" /></td>
2429 </tr>
2430
2431 <tr>
2432 <th scope="row"><?php esc_html_e( 'Icon', 'jetpack' ); ?></th>
2433 <td><input type="text" name="icon" value="<?php echo esc_attr( $this->icon ); ?>" /></td>
2434 </tr>
2435
2436 <tr>
2437 <th scope="row"></th>
2438 <td>
2439 <input class="button-secondary" type="submit" value="<?php esc_attr_e( 'Save', 'jetpack' ); ?>" />
2440 <a href="#" class="remove"><small><?php esc_html_e( 'Remove Service', 'jetpack' ); ?></small></a>
2441 </td>
2442 </tr>
2443 </tbody>
2444 </table>
2445 </div>
2446 <?php
2447 }
2448
2449 /**
2450 * Sanitize and save options for our sharing buttons.
2451 *
2452 * @param array $data Data to be saved.
2453 *
2454 * @return void
2455 */
2456 public function update_options( array $data ) {
2457 $name = trim( wp_html_excerpt( wp_kses( stripslashes( $data['name'] ), array() ), 30 ) );
2458 $url = trim( esc_url_raw( $data['url'] ) );
2459 $icon = trim( esc_url_raw( $data['icon'] ) );
2460
2461 if ( $name ) {
2462 $this->name = $name;
2463 }
2464
2465 if ( $url ) {
2466 $this->url = $url;
2467 }
2468
2469 if ( $icon ) {
2470 $this->icon = $icon;
2471 }
2472 }
2473
2474 /**
2475 * Get array of information about the service.
2476 *
2477 * @return array
2478 */
2479 public function get_options() {
2480 return array(
2481 'name' => $this->name,
2482 'icon' => $this->icon,
2483 'url' => $this->url,
2484 );
2485 }
2486
2487 /**
2488 * Display a preview of the sharing button.
2489 *
2490 * @param bool $echo Whether to echo the output or return it.
2491 * @param bool $force_smart Whether to force the smart (official) services to be shown.
2492 * @param null|string $button_style Button style.
2493 *
2494 * @return void
2495 */
2496 public function display_preview( $echo = true, $force_smart = false, $button_style = null ) { // phpcs:ignore VariableAnalysis.CodeAnalysis.VariableAnalysis.UnusedVariable
2497 $opts = $this->get_options();
2498
2499 $text = '&nbsp;';
2500 if ( ! $this->smart ) {
2501 if ( $this->button_style !== 'icon' ) {
2502 $text = $this->get_name();
2503 }
2504 }
2505
2506 $klasses = array( 'share-' . $this->shortname );
2507
2508 if ( $this->button_style === 'icon' || $this->button_style === 'icon-text' ) {
2509 $klasses[] = 'share-icon';
2510 }
2511
2512 if ( $this->button_style === 'icon' ) {
2513 $text = '';
2514 $klasses[] = 'no-text';
2515 }
2516
2517 if ( $this->button_style === 'text' ) {
2518 $klasses[] = 'no-icon';
2519 }
2520
2521 $link = sprintf(
2522 '<a rel="nofollow" class="%s" href="javascript:void(0)" title="%s"><span style="background-image:url(&quot;%s&quot;) !important;background-position:left center;background-repeat:no-repeat;">%s</span></a>',
2523 esc_attr( implode( ' ', $klasses ) ),
2524 esc_attr( $this->get_name() ),
2525 addcslashes( esc_url_raw( $opts['icon'] ), '"' ),
2526 esc_html( $text )
2527 );
2528 ?>
2529 <div class="option option-smart-off">
2530 <?php echo $link; // phpcs:ignore WordPress.Security.EscapeOutput.OutputNotEscaped -- escaped above. ?>
2531 </div>
2532 <?php
2533 }
2534 }
2535
2536 /**
2537 * Tumblr sharing service.
2538 */
2539 class Share_Tumblr extends Sharing_Source {
2540 /**
2541 * Service short name.
2542 *
2543 * @var string
2544 */
2545 public $shortname = 'tumblr';
2546
2547 /**
2548 * Service icon font code.
2549 *
2550 * @var string
2551 */
2552 public $icon = '\f214';
2553
2554 /**
2555 * Constructor.
2556 *
2557 * @param int $id Sharing source ID.
2558 * @param array $settings Sharing settings.
2559 */
2560 public function __construct( $id, array $settings ) {
2561 parent::__construct( $id, $settings );
2562 if ( 'official' === $this->button_style ) {
2563 $this->smart = true;
2564 } else {
2565 $this->smart = false;
2566 }
2567 }
2568
2569 /**
2570 * Service name.
2571 *
2572 * @return string
2573 */
2574 public function get_name() {
2575 return __( 'Tumblr', 'jetpack' );
2576 }
2577
2578 /**
2579 * Get the markup of the sharing button.
2580 *
2581 * @param WP_Post $post Post object.
2582 *
2583 * @return string
2584 */
2585 public function get_display( $post ) {
2586 if ( $this->smart ) {
2587 $target = '';
2588 if ( true === $this->open_link_in_new ) {
2589 $target = '_blank';
2590 }
2591
2592 /**
2593 * If we are looking at a single post, let Tumblr figure out the post type (text, photo, link, quote, chat, or video)
2594 * based on the content available on the page.
2595 * If we are not looking at a single post, content from other posts can appear on the page and Tumblr will pick that up.
2596 * In this case, we want Tumblr to focus on our current post, so we will limit the post type to link, where we can give Tumblr a link to our post.
2597 */
2598 if ( ! is_single() ) {
2599 $posttype = 'data-posttype="link"';
2600 } else {
2601 $posttype = '';
2602 }
2603
2604 // Documentation: https://www.tumblr.com/docs/en/share_button
2605 return sprintf(
2606 '<a class="tumblr-share-button" target="%1$s" href="%2$s" data-title="%3$s" data-content="%4$s" title="%5$s"%6$s>%5$s</a>',
2607 $target,
2608 'https://www.tumblr.com/share',
2609 $this->get_share_title( $post->ID ),
2610 $this->get_share_url( $post->ID ),
2611 __( 'Share on Tumblr', 'jetpack' ),
2612 $posttype
2613 );
2614 } else {
2615 return $this->get_link( $this->get_process_request_url( $post->ID ), _x( 'Tumblr', 'share to', 'jetpack' ), __( 'Click to share on Tumblr', 'jetpack' ), 'share=tumblr', 'sharing-tumblr-' . $post->ID );
2616 }
2617 }
2618
2619 /**
2620 * Process sharing request. Add actions that need to happen when sharing here.
2621 *
2622 * @param WP_Post $post Post object.
2623 * @param array $post_data Array of information about the post we're sharing.
2624 *
2625 * @return void
2626 */
2627 public function process_request( $post, array $post_data ) {
2628 // Record stats
2629 parent::process_request( $post, $post_data );
2630
2631 // Redirect to Tumblr's sharing endpoint (a la their bookmarklet)
2632 $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=';
2633
2634 parent::redirect_request( $url );
2635 }
2636
2637 /**
2638 * Add content specific to a service in the footer.
2639 */
2640 public function display_footer() {
2641 if ( $this->smart ) {
2642 // phpcs:disable WordPress.WP.EnqueuedResources.NonEnqueuedScript
2643 ?>
2644 <script id="tumblr-js" type="text/javascript" src="https://assets.tumblr.com/share-button.js"></script>
2645 <?php
2646 // phpcs:enable WordPress.WP.EnqueuedResources.NonEnqueuedScript
2647 } else {
2648 $this->js_dialog(
2649 $this->shortname,
2650 array(
2651 'width' => 450,
2652 'height' => 450,
2653 )
2654 );
2655 }
2656 }
2657 }
2658
2659 /**
2660 * Pinterest sharing service.
2661 */
2662 class Share_Pinterest extends Sharing_Source {
2663 /**
2664 * Service short name.
2665 *
2666 * @var string
2667 */
2668 public $shortname = 'pinterest';
2669
2670 /**
2671 * Service icon font code.
2672 *
2673 * @var string
2674 */
2675 public $icon = '\f209';
2676
2677 /**
2678 * Constructor.
2679 *
2680 * @param int $id Sharing source ID.
2681 * @param array $settings Sharing settings.
2682 */
2683 public function __construct( $id, array $settings ) {
2684 parent::__construct( $id, $settings );
2685 if ( 'official' === $this->button_style ) {
2686 $this->smart = true;
2687 } else {
2688 $this->smart = false;
2689 }
2690 }
2691
2692 /**
2693 * Service name.
2694 *
2695 * @return string
2696 */
2697 public function get_name() {
2698 return __( 'Pinterest', 'jetpack' );
2699 }
2700
2701 /**
2702 * Get image representative of the post to pass on to Pinterest.
2703 *
2704 * @param WP_Post $post Post object.
2705 *
2706 * @return string
2707 */
2708 public function get_image( $post ) {
2709 if ( class_exists( 'Jetpack_PostImages' ) ) {
2710 $image = Jetpack_PostImages::get_image( $post->ID, array( 'fallback_to_avatars' => true ) );
2711 if ( ! empty( $image ) ) {
2712 return $image['src'];
2713 }
2714 }
2715
2716 /**
2717 * Filters the default image used by the Pinterest Pin It share button.
2718 *
2719 * @module sharedaddy
2720 *
2721 * @since 3.6.0
2722 *
2723 * @param string $url Default image URL.
2724 */
2725 return apply_filters( 'jetpack_sharing_pinterest_default_image', 'https://s0.wp.com/i/blank.jpg' );
2726 }
2727
2728 /**
2729 * Get Pinterest external sharing URL.
2730 *
2731 * @param WP_Post $post Post object.
2732 *
2733 * @return string
2734 */
2735 public function get_external_url( $post ) {
2736 $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 );
2737
2738 /**
2739 * Filters the Pinterest share URL used in sharing button output.
2740 *
2741 * @module sharedaddy
2742 *
2743 * @since 3.6.0
2744 *
2745 * @param string $url Pinterest share URL.
2746 */
2747 return apply_filters( 'jetpack_sharing_pinterest_share_url', $url );
2748 }
2749
2750 /**
2751 * Get Pinterest widget type.
2752 *
2753 * @return string
2754 */
2755 public function get_widget_type() {
2756 /**
2757 * Filters the Pinterest widget type.
2758 *
2759 * @see https://business.pinterest.com/en/widget-builder
2760 *
2761 * @module sharedaddy
2762 *
2763 * @since 3.6.0
2764 *
2765 * @param string $type Pinterest widget type. Default of 'buttonPin' for single-image selection. 'buttonBookmark' for multi-image modal.
2766 */
2767 return apply_filters( 'jetpack_sharing_pinterest_widget_type', 'buttonPin' );
2768 }
2769
2770 /**
2771 * Get the markup of the sharing button.
2772 *
2773 * @param WP_Post $post Post object.
2774 *
2775 * @return string
2776 */
2777 public function get_display( $post ) {
2778 $display = '';
2779
2780 if ( $this->smart ) {
2781 $display = sprintf(
2782 '<div class="pinterest_button"><a href="%s" data-pin-do="%s" data-pin-config="beside"><img src="//assets.pinterest.com/images/pidgets/pinit_fg_en_rect_gray_20.png" /></a></div>',
2783 esc_url( $this->get_external_url( $post ) ),
2784 esc_attr( $this->get_widget_type() )
2785 );
2786 } else {
2787 $display = $this->get_link( $this->get_process_request_url( $post->ID ), _x( 'Pinterest', 'share to', 'jetpack' ), __( 'Click to share on Pinterest', 'jetpack' ), 'share=pinterest', 'sharing-pinterest-' . $post->ID );
2788 }
2789
2790 /** This filter is already documented in modules/sharedaddy/sharing-sources.php */
2791 if ( apply_filters( 'jetpack_register_post_for_share_counts', true, $post->ID, 'linkedin' ) ) {
2792 sharing_register_post_for_share_counts( $post->ID );
2793 }
2794
2795 return $display;
2796 }
2797
2798 /**
2799 * Process sharing request. Add actions that need to happen when sharing here.
2800 *
2801 * @param WP_Post $post Post object.
2802 * @param array $post_data Array of information about the post we're sharing.
2803 *
2804 * @return void
2805 */
2806 public function process_request( $post, array $post_data ) {
2807 // Record stats
2808 parent::process_request( $post, $post_data );
2809 // If we're triggering the multi-select panel, then we don't need to redirect to Pinterest
2810 if ( ! isset( $_GET['js_only'] ) ) { // phpcs:ignore WordPress.Security.NonceVerification.Recommended
2811 $pinterest_url = esc_url_raw( $this->get_external_url( $post ) );
2812 parent::redirect_request( $pinterest_url );
2813 } else {
2814 echo '// share count bumped';
2815 die( 0 );
2816 }
2817 }
2818
2819 /**
2820 * Add content specific to a service in the footer.
2821 */
2822 public function display_footer() {
2823 /**
2824 * Filter the Pin it button appearing when hovering over images when using the official button style.
2825 *
2826 * @module sharedaddy
2827 *
2828 * @since 3.6.0
2829 *
2830 * @param bool $jetpack_pinit_over True by default, displays the Pin it button when hovering over images.
2831 */
2832 $jetpack_pinit_over = apply_filters( 'jetpack_pinit_over_button', true );
2833 ?>
2834 <?php if ( $this->smart ) : ?>
2835 <script type="text/javascript">
2836 ( function () {
2837 // Pinterest shared resources
2838 var s = document.createElement( 'script' );
2839 s.type = 'text/javascript';
2840 s.async = true;
2841 <?php
2842 if ( $jetpack_pinit_over ) {
2843 echo "s.setAttribute( 'data-pin-hover', true );";
2844 }
2845 ?>
2846 s.src = window.location.protocol + '//assets.pinterest.com/js/pinit.js';
2847 var x = document.getElementsByTagName( 'script' )[ 0 ];
2848 x.parentNode.insertBefore(s, x);
2849 // if 'Pin it' button has 'counts' make container wider
2850 function init() {
2851 var shares = document.querySelectorAll( 'li.share-pinterest' );
2852 for ( var i = 0; i < shares.length; i++ ) {
2853 var share = shares[ i ];
2854 var countElement = share.querySelector( 'a span' );
2855 if (countElement) {
2856 var countComputedStyle = window.getComputedStyle(countElement);
2857 if ( countComputedStyle.display === 'block' ) {
2858 var countWidth = parseInt( countComputedStyle.width, 10 );
2859 share.style.marginRight = countWidth + 11 + 'px';
2860 }
2861 }
2862 }
2863 }
2864
2865 if ( document.readyState !== 'complete' ) {
2866 document.addEventListener( 'load', init );
2867 } else {
2868 init();
2869 }
2870 } )();
2871 </script>
2872 <?php elseif ( 'buttonPin' !== $this->get_widget_type() ) : ?>
2873 <script type="text/javascript">
2874 ( function () {
2875 function init() {
2876 document.body.addEventListener( 'click', function ( e ) {
2877 if ( e.target && (
2878 e.target.matches && e.target.matches( 'a.share-pinterest' ) ||
2879 e.target.msMatchesSelector && e.target.msMatchesSelector( 'a.share-pinterest' )
2880 ) ) {
2881 e.preventDefault();
2882 // Load Pinterest Bookmarklet code
2883 var s = document.createElement( 'script' );
2884 s.type = 'text/javascript';
2885 s.src = window.location.protocol + '//assets.pinterest.com/js/pinmarklet.js?r=' + ( Math.random() * 99999999 );
2886 var x = document.getElementsByTagName( 'script' )[ 0 ];
2887 x.parentNode.insertBefore( s, x );
2888 // Trigger Stats
2889 var s = document.createElement( 'script' );
2890 s.type = 'text/javascript';
2891 s.src = e.target.href + ( e.target.href.indexOf( '?' ) ? '&' : '?' ) + 'js_only=1';
2892 var x = document.getElementsByTagName( 'script' )[ '0' ];
2893 x.parentNode.insertBefore( s, x );
2894 }
2895 } );
2896 }
2897
2898 if ( document.readyState === 'loading' ) {
2899 document.addEventListener( 'DOMContentLoaded', init );
2900 } else {
2901 init();
2902 }
2903 } )();
2904 </script>
2905 <?php
2906 endif;
2907 }
2908 }
2909
2910 /**
2911 * Pocket sharing service.
2912 */
2913 class Share_Pocket extends Sharing_Source {
2914 /**
2915 * Service short name.
2916 *
2917 * @var string
2918 */
2919 public $shortname = 'pocket';
2920
2921 /**
2922 * Service icon font code.
2923 *
2924 * @var string
2925 */
2926 public $icon = '\f224';
2927
2928 /**
2929 * Constructor.
2930 *
2931 * @param int $id Sharing source ID.
2932 * @param array $settings Sharing settings.
2933 */
2934 public function __construct( $id, array $settings ) {
2935 parent::__construct( $id, $settings );
2936
2937 if ( 'official' === $this->button_style ) {
2938 $this->smart = true;
2939 } else {
2940 $this->smart = false;
2941 }
2942 }
2943
2944 /**
2945 * Service name.
2946 *
2947 * @return string
2948 */
2949 public function get_name() {
2950 return __( 'Pocket', 'jetpack' );
2951 }
2952
2953 /**
2954 * Process sharing request. Add actions that need to happen when sharing here.
2955 *
2956 * @param WP_Post $post Post object.
2957 * @param array $post_data Array of information about the post we're sharing.
2958 *
2959 * @return void
2960 */
2961 public function process_request( $post, array $post_data ) {
2962 // Record stats
2963 parent::process_request( $post, $post_data );
2964
2965 $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 ) ) );
2966
2967 parent::redirect_request( $pocket_url );
2968 }
2969
2970 /**
2971 * Get the markup of the sharing button.
2972 *
2973 * @param WP_Post $post Post object.
2974 *
2975 * @return string
2976 */
2977 public function get_display( $post ) {
2978 if ( $this->smart ) {
2979 $post_count = 'horizontal';
2980
2981 $button = '';
2982 $button .= '<div class="pocket_button">';
2983 $button .= sprintf( '<a href="https://getpocket.com/save" class="pocket-btn" data-lang="%s" data-save-url="%s" data-pocket-count="%s" >%s</a>', 'en', esc_attr( $this->get_share_url( $post->ID ) ), $post_count, esc_attr__( 'Pocket', 'jetpack' ) );
2984 $button .= '</div>';
2985
2986 return $button;
2987 } else {
2988 return $this->get_link( $this->get_process_request_url( $post->ID ), _x( 'Pocket', 'share to', 'jetpack' ), __( 'Click to share on Pocket', 'jetpack' ), 'share=pocket', 'sharing-pocket-' . $post->ID );
2989 }
2990 }
2991
2992 /**
2993 * AMP display for Pocket.
2994 *
2995 * @param \WP_Post $post The current post being viewed.
2996 */
2997 public function get_amp_display( $post ) {
2998 $attrs = array(
2999 'data-share-endpoint' => esc_url_raw( 'https://getpocket.com/save/?url=' . rawurlencode( $this->get_share_url( $post->ID ) ) . '&title=' . rawurlencode( $this->get_share_title( $post->ID ) ) ),
3000 );
3001
3002 return $this->build_amp_markup( $attrs );
3003 }
3004
3005 /**
3006 * Add content specific to a service in the footer.
3007 */
3008 public function display_footer() {
3009 if ( $this->smart ) :
3010 ?>
3011 <script>
3012 ( function () {
3013 var currentScript = document.currentScript;
3014
3015 // Don't use Pocket's default JS as it we need to force init new Pocket share buttons loaded via JS.
3016 function jetpack_sharing_pocket_init() {
3017 var script = document.createElement( 'script' );
3018 var prev = currentScript || document.getElementsByTagName( 'script' )[ 0 ];
3019 script.setAttribute( 'async', true );
3020 script.setAttribute( 'src', 'https://widgets.getpocket.com/v1/j/btn.js?v=1' );
3021 prev.parentNode.insertBefore( script, prev );
3022 }
3023
3024 if ( document.readyState === 'loading' ) {
3025 document.addEventListener( 'DOMContentLoaded', jetpack_sharing_pocket_init );
3026 } else {
3027 jetpack_sharing_pocket_init();
3028 }
3029 document.body.addEventListener( 'is.post-load', jetpack_sharing_pocket_init );
3030 } )();
3031 </script>
3032 <?php
3033 else :
3034 $this->js_dialog(
3035 $this->shortname,
3036 array(
3037 'width' => 450,
3038 'height' => 450,
3039 )
3040 );
3041 endif;
3042 }
3043 }
3044
3045 /**
3046 * Telegram sharing service.
3047 */
3048 class Share_Telegram extends Sharing_Source {
3049 /**
3050 * Service short name.
3051 *
3052 * @var string
3053 */
3054 public $shortname = 'telegram';
3055
3056 /**
3057 * Constructor.
3058 *
3059 * @param int $id Sharing source ID.
3060 * @param array $settings Sharing settings.
3061 */
3062 public function __construct( $id, array $settings ) { // phpcs:ignore Generic.CodeAnalysis.UselessOverridingMethod.Found
3063 parent::__construct( $id, $settings );
3064 }
3065
3066 /**
3067 * Service name.
3068 *
3069 * @return string
3070 */
3071 public function get_name() {
3072 return __( 'Telegram', 'jetpack' );
3073 }
3074
3075 /**
3076 * Process sharing request. Add actions that need to happen when sharing here.
3077 *
3078 * @param WP_Post $post Post object.
3079 * @param array $post_data Array of information about the post we're sharing.
3080 *
3081 * @return void
3082 */
3083 public function process_request( $post, array $post_data ) {
3084 // Record stats
3085 parent::process_request( $post, $post_data );
3086
3087 $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 ) ) );
3088
3089 parent::redirect_request( $telegram_url );
3090 }
3091
3092 /**
3093 * Get the markup of the sharing button.
3094 *
3095 * @param WP_Post $post Post object.
3096 *
3097 * @return string
3098 */
3099 public function get_display( $post ) {
3100 return $this->get_link( $this->get_process_request_url( $post->ID ), _x( 'Telegram', 'share to', 'jetpack' ), __( 'Click to share on Telegram', 'jetpack' ), 'share=telegram', 'sharing-telegram-' . $post->ID );
3101 }
3102
3103 /**
3104 * AMP display for Telegram.
3105 *
3106 * @param \WP_Post $post The current post being viewed.
3107 */
3108 public function get_amp_display( $post ) {
3109 $attrs = array(
3110 'data-share-endpoint' => esc_url_raw( 'https://telegram.me/share/url?url=' . rawurlencode( $this->get_share_url( $post->ID ) ) . '&text=' . rawurlencode( $this->get_share_title( $post->ID ) ) ),
3111 );
3112
3113 return $this->build_amp_markup( $attrs );
3114 }
3115
3116 /**
3117 * Add content specific to a service in the footer.
3118 */
3119 public function display_footer() {
3120 $this->js_dialog(
3121 $this->shortname,
3122 array(
3123 'width' => 450,
3124 'height' => 450,
3125 )
3126 );
3127 }
3128 }
3129
3130 /**
3131 * WhatsApp sharing service.
3132 */
3133 class Jetpack_Share_WhatsApp extends Sharing_Source {
3134 /**
3135 * Service short name.
3136 *
3137 * @var string
3138 */
3139 public $shortname = 'jetpack-whatsapp';
3140
3141 /**
3142 * Constructor.
3143 *
3144 * @param int $id Sharing source ID.
3145 * @param array $settings Sharing settings.
3146 */
3147 public function __construct( $id, array $settings ) { // phpcs:ignore Generic.CodeAnalysis.UselessOverridingMethod.Found
3148 parent::__construct( $id, $settings );
3149 }
3150
3151 /**
3152 * Service name.
3153 *
3154 * @return string
3155 */
3156 public function get_name() {
3157 return __( 'WhatsApp', 'jetpack' );
3158 }
3159
3160 /**
3161 * Get the markup of the sharing button.
3162 *
3163 * @param WP_Post $post Post object.
3164 *
3165 * @return string
3166 */
3167 public function get_display( $post ) {
3168 return $this->get_link( $this->get_process_request_url( $post->ID ), _x( 'WhatsApp', 'share to', 'jetpack' ), __( 'Click to share on WhatsApp', 'jetpack' ), 'share=jetpack-whatsapp', 'sharing-whatsapp-' . $post->ID );
3169 }
3170
3171 /**
3172 * AMP display for Whatsapp.
3173 *
3174 * @param \WP_Post $post The current post being viewed.
3175 */
3176 public function get_amp_display( $post ) { // phpcs:ignore VariableAnalysis.CodeAnalysis.VariableAnalysis.UnusedVariable
3177 $attrs = array(
3178 'type' => 'whatsapp',
3179 );
3180
3181 return $this->build_amp_markup( $attrs );
3182 }
3183
3184 /**
3185 * Process sharing request. Add actions that need to happen when sharing here.
3186 *
3187 * @param WP_Post $post Post object.
3188 * @param array $post_data Array of information about the post we're sharing.
3189 *
3190 * @return void
3191 */
3192 public function process_request( $post, array $post_data ) {
3193 // Record stats
3194 parent::process_request( $post, $post_data );
3195
3196 // Firefox for desktop doesn't handle the "api.whatsapp.com" URL properly, so use "web.whatsapp.com"
3197 if ( User_Agent_Info::is_firefox_desktop() ) {
3198 $url = 'https://web.whatsapp.com/send?text=';
3199 } else {
3200 $url = 'https://api.whatsapp.com/send?text=';
3201 }
3202
3203 $url .= rawurlencode( $this->get_share_title( $post->ID ) . ' ' . $this->get_share_url( $post->ID ) );
3204
3205 parent::redirect_request( $url );
3206 }
3207 }
3208
3209 /**
3210 * Mastodon sharing service.
3211 */
3212 class Share_Mastodon extends Sharing_Source {
3213 /**
3214 * Service short name.
3215 *
3216 * @var string
3217 */
3218 public $shortname = 'mastodon';
3219
3220 /**
3221 * Service icon font code.
3222 *
3223 * @var string
3224 */
3225 public $icon = '\f10a';
3226
3227 /**
3228 * Service name.
3229 *
3230 * @return string
3231 */
3232 public function get_name() {
3233 return __( 'Mastodon', 'jetpack' );
3234 }
3235
3236 /**
3237 * Get the markup of the sharing button.
3238 *
3239 * @param WP_Post $post Post object.
3240 *
3241 * @return string
3242 */
3243 public function get_display( $post ) {
3244 return $this->get_link(
3245 $this->get_process_request_url( $post->ID ),
3246 _x( 'Mastodon', 'share to', 'jetpack' ),
3247 __( 'Click to share on Mastodon', 'jetpack' ),
3248 'share=mastodon',
3249 'sharing-mastodon-' . $post->ID
3250 );
3251 }
3252
3253 /**
3254 * Process sharing request. Add actions that need to happen when sharing here.
3255 *
3256 * @param WP_Post $post Post object.
3257 * @param array $post_data Array of information about the post we're sharing.
3258 *
3259 * @return void
3260 */
3261 public function process_request( $post, array $post_data ) {
3262 if ( empty( $_POST['jetpack-mastodon-instance'] ) ) {
3263 require_once WP_SHARING_PLUGIN_DIR . 'services/class-jetpack-mastodon-modal.php';
3264 add_action( 'template_redirect', array( 'Jetpack_Mastodon_Modal', 'modal' ) );
3265 return;
3266 }
3267
3268 check_admin_referer( 'jetpack_share_mastodon_instance' );
3269
3270 $mastodon_instance = isset( $_POST['jetpack-mastodon-instance'] )
3271 ? trailingslashit( sanitize_text_field( wp_unslash( $_POST['jetpack-mastodon-instance'] ) ) )
3272 : null;
3273
3274 $post_title = $this->get_share_title( $post->ID );
3275 $post_link = $this->get_share_url( $post->ID );
3276 $post_tags = $this->get_share_tags( $post->ID );
3277
3278 /**
3279 * Allow filtering the default message that gets posted to Mastodon.
3280 *
3281 * @module sharedaddy
3282 * @since 11.9
3283 *
3284 * @param string $share_url The default message that gets posted to Mastodon.
3285 * @param WP_Post $post The post object.
3286 * @param array $post_data Array of information about the post we're sharing.
3287 */
3288 $shared_message = apply_filters(
3289 'jetpack_sharing_mastodon_default_message',
3290 $post_title . ' ' . $post_link . ' ' . $post_tags,
3291 $post,
3292 $post_data
3293 );
3294
3295 $share_url = sprintf(
3296 '%1$sshare?text=%2$s',
3297 $mastodon_instance,
3298 rawurlencode( $shared_message )
3299 );
3300
3301 // Record stats
3302 parent::process_request( $post, $post_data );
3303
3304 parent::redirect_request( $share_url );
3305 }
3306
3307 /**
3308 * Add content specific to a service in the footer.
3309 */
3310 public function display_footer() {
3311 $this->js_dialog(
3312 $this->shortname,
3313 array(
3314 'width' => 460,
3315 'height' => 400,
3316 )
3317 );
3318 }
3319 }
3320
3321 /**
3322 * Nextdoor sharing service.
3323 */
3324 class Share_Nextdoor extends Sharing_Source {
3325 /**
3326 * Service short name.
3327 *
3328 * @var string
3329 */
3330 public $shortname = 'nextdoor';
3331
3332 /**
3333 * Service icon font code.
3334 *
3335 * @var string
3336 */
3337 public $icon = '\f10c';
3338
3339 /**
3340 * Service name.
3341 *
3342 * @return string
3343 */
3344 public function get_name() {
3345 return __( 'Nextdoor', 'jetpack' );
3346 }
3347
3348 /**
3349 * Get the markup of the sharing button.
3350 *
3351 * @param WP_Post $post Post object.
3352 *
3353 * @return string
3354 */
3355 public function get_display( $post ) {
3356 return $this->get_link(
3357 $this->get_process_request_url( $post->ID ),
3358 _x( 'Nextdoor', 'share to', 'jetpack' ),
3359 __( 'Click to share on Nextdoor', 'jetpack' ),
3360 'share=nextdoor',
3361 'sharing-nextdoor-' . $post->ID
3362 );
3363 }
3364
3365 /**
3366 * Process sharing request. Add actions that need to happen when sharing here.
3367 *
3368 * @param WP_Post $post Post object.
3369 * @param array $post_data Array of information about the post we're sharing.
3370 *
3371 * @return void
3372 */
3373 public function process_request( $post, array $post_data ) {
3374 // Record stats
3375 parent::process_request( $post, $post_data );
3376
3377 $url = 'https://nextdoor.com/sharekit/?source=jetpack&body=';
3378 $url .= rawurlencode( $this->get_share_title( $post->ID ) . ' ' . $this->get_share_url( $post->ID ) );
3379
3380 parent::redirect_request( $url );
3381 }
3382 }
3383
3384 /**
3385 * Threads sharing service.
3386 */
3387 class Share_Threads extends Sharing_Source {
3388 /**
3389 * Service short name.
3390 *
3391 * @var string
3392 */
3393 public $shortname = 'threads';
3394
3395 /**
3396 * Service icon font code.
3397 *
3398 * @var string
3399 */
3400 public $icon = '\f10d';
3401
3402 /**
3403 * Service name.
3404 *
3405 * @return string
3406 */
3407 public function get_name() {
3408 return __( 'Threads', 'jetpack' );
3409 }
3410
3411 /**
3412 * Get the markup of the sharing button.
3413 *
3414 * @param WP_Post $post Post object.
3415 *
3416 * @return string
3417 */
3418 public function get_display( $post ) {
3419 return $this->get_link(
3420 $this->get_process_request_url( $post->ID ),
3421 _x( 'Threads', 'share to', 'jetpack' ),
3422 __( 'Click to share on Threads', 'jetpack' ),
3423 'share=threads',
3424 'sharing-threads-' . $post->ID
3425 );
3426 }
3427
3428 /**
3429 * Process sharing request. Add actions that need to happen when sharing here.
3430 *
3431 * @param WP_Post $post Post object.
3432 * @param array $post_data Array of information about the post we're sharing.
3433 *
3434 * @return void
3435 */
3436 public function process_request( $post, array $post_data ) {
3437 // Record stats
3438 parent::process_request( $post, $post_data );
3439
3440 $url = 'https://threads.net/intent/post?text=';
3441 $url .= rawurlencode( $this->get_share_title( $post->ID ) . ' ' . $this->get_share_url( $post->ID ) );
3442
3443 parent::redirect_request( $url );
3444 }
3445
3446 /**
3447 * Add content specific to a service in the footer.
3448 */
3449 public function display_footer() {
3450 $this->js_dialog( $this->shortname );
3451 }
3452 }
3453
3454 /**
3455 * Bluesky sharing service.
3456 */
3457 class Share_Bluesky extends Sharing_Source {
3458 /**
3459 * Service short name.
3460 *
3461 * @var string
3462 */
3463 public $shortname = 'bluesky';
3464
3465 /**
3466 * Service icon font code.
3467 *
3468 * @var string
3469 */
3470 public $icon = '\f10f';
3471
3472 /**
3473 * Service name.
3474 *
3475 * @return string
3476 */
3477 public function get_name() {
3478 return __( 'Bluesky', 'jetpack' );
3479 }
3480
3481 /**
3482 * Get the markup of the sharing button.
3483 *
3484 * @param WP_Post $post Post object.
3485 *
3486 * @return string
3487 */
3488 public function get_display( $post ) {
3489 return $this->get_link(
3490 $this->get_process_request_url( $post->ID ),
3491 _x( 'Bluesky', 'share to', 'jetpack' ),
3492 __( 'Click to share on Bluesky', 'jetpack' ),
3493 'share=bluesky',
3494 'sharing-bluesky-' . $post->ID
3495 );
3496 }
3497
3498 /**
3499 * Process sharing request. Add actions that need to happen when sharing here.
3500 *
3501 * @param WP_Post $post Post object.
3502 * @param array $post_data Array of information about the post we're sharing.
3503 *
3504 * @return void
3505 */
3506 public function process_request( $post, array $post_data ) {
3507 // Record stats
3508 parent::process_request( $post, $post_data );
3509
3510 $url = 'https://bsky.app/intent/compose?text=';
3511 $url .= rawurlencode( $this->get_share_title( $post->ID ) . ' ' . $this->get_share_url( $post->ID ) );
3512
3513 parent::redirect_request( $url );
3514 }
3515
3516 /**
3517 * Add content specific to a service in the footer.
3518 */
3519 public function display_footer() {
3520 $this->js_dialog( $this->shortname );
3521 }
3522 }
3523