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

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

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