PluginProbe
Jetpack – WP Security, Backup, Speed, & Growth / 16.2
Jetpack – WP Security, Backup, Speed, & Growth v16.2
16.2 16.2-beta 12.0.3 12.1.3 12.2.3 12.3.2 12.4.2 12.5.2 12.6.4 12.7.3 12.8.3 12.9.5 13.0.2 13.1.5 13.2.4 13.3.3 13.4.5 13.5.2 13.6.2 13.7.2 13.8.3 13.9.2 14.0.1 14.1.1 14.2.2 All 502 releases
jetpack / modules / sharedaddy / sharing-sources.php

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

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