PluginProbe
Jetpack – WP Security, Backup, Speed, & Growth / 9.2
Jetpack – WP Security, Backup, Speed, & Growth v9.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 14.3.1 All 501 releases
jetpack / modules / sharedaddy / sharing-sources.php
sharing-sources.php
1,985 lines 58.1 KB
No matching file
Up and down to move Enter to open Esc to close
Raw Download Zip
1 <?php
2
3 use Automattic\Jetpack\Device_Detection\User_Agent_Info;
4
5 abstract class Sharing_Source {
6 public $button_style;
7 public $smart;
8 protected $open_link_in_new;
9 protected $id;
10
11 public function __construct( $id, array $settings ) {
12 $this->id = $id;
13 /**
14 * Filter the way sharing links open.
15 *
16 * By default, sharing links open in a new window.
17 *
18 * @module sharedaddy
19 *
20 * @since 3.4.0
21 *
22 * @param bool true Should Sharing links open in a new window. Default to true.
23 */
24 $this->open_link_in_new = apply_filters( 'jetpack_open_sharing_in_new_window', true );
25
26 if ( isset( $settings['button_style'] ) ) {
27 $this->button_style = $settings['button_style'];
28 }
29
30 if ( isset( $settings['smart'] ) ) {
31 $this->smart = $settings['smart'];
32 }
33 }
34
35 public function is_deprecated() {
36 return false;
37 }
38
39 public function http() {
40 return is_ssl() ? 'https' : 'http';
41 }
42
43 public function get_id() {
44 return $this->id;
45 }
46
47 public function get_class() {
48 return $this->id;
49 }
50
51 public function get_share_url( $post_id ) {
52 /**
53 * Filter the sharing permalink.
54 *
55 * @module sharedaddy
56 *
57 * @since 1.2.0
58 *
59 * @param string get_permalink( $post_id ) Post Permalink.
60 * @param int $post_id Post ID.
61 * @param int $this->id Sharing ID.
62 */
63 return apply_filters( 'sharing_permalink', get_permalink( $post_id ), $post_id, $this->id );
64 }
65
66 public function get_share_title( $post_id ) {
67 $post = get_post( $post_id );
68 /**
69 * Filter the sharing title.
70 *
71 * @module sharedaddy
72 *
73 * @since 2.8.0
74 *
75 * @param string $post->post_title Post Title.
76 * @param int $post_id Post ID.
77 * @param int $this->id Sharing ID.
78 */
79 $title = apply_filters( 'sharing_title', $post->post_title, $post_id, $this->id );
80
81 return html_entity_decode( wp_kses( $title, null ) );
82 }
83
84 public function has_custom_button_style() {
85 return false;
86 }
87
88 public function get_link( $url, $text, $title, $query = '', $id = false ) {
89 $args = func_get_args();
90 $klasses = array( 'share-' . $this->get_class(), 'sd-button' );
91
92 if ( 'icon' == $this->button_style || 'icon-text' == $this->button_style ) {
93 $klasses[] = 'share-icon';
94 }
95
96 if ( 'icon' == $this->button_style ) {
97 $text = $title;
98 $klasses[] = 'no-text';
99
100 if ( true == $this->open_link_in_new ) {
101 $text .= __( ' (Opens in new window)', 'jetpack' );
102 }
103 }
104
105 /**
106 * Filter the sharing display ID.
107 *
108 * @module sharedaddy
109 *
110 * @since 3.4.0
111 *
112 * @param int|false $id Sharing ID.
113 * @param object $this Sharing service properties.
114 * @param array $args Array of sharing service options.
115 */
116 $id = apply_filters( 'jetpack_sharing_display_id', $id, $this, $args );
117 /**
118 * Filter the sharing display link.
119 *
120 * @module sharedaddy
121 *
122 * @since 2.8.0
123 *
124 * @param string $url Post URL.
125 * @param object $this Sharing service properties.
126 * @param int|false $id Sharing ID.
127 * @param array $args Array of sharing service options.
128 */
129 $url = apply_filters( 'sharing_display_link', $url, $this, $id, $args ); // backwards compatibility
130 /**
131 * Filter the sharing display link.
132 *
133 * @module sharedaddy
134 *
135 * @since 2.8.0
136 *
137 * @param string $url Post URL.
138 * @param object $this Sharing service properties.
139 * @param int|false $id Sharing ID.
140 * @param array $args Array of sharing service options.
141 */
142 $url = apply_filters( 'jetpack_sharing_display_link', $url, $this, $id, $args );
143 /**
144 * Filter the sharing display query.
145 *
146 * @module sharedaddy
147 *
148 * @since 2.8.0
149 *
150 * @param string $query Sharing service URL parameter.
151 * @param object $this Sharing service properties.
152 * @param int|false $id Sharing ID.
153 * @param array $args Array of sharing service options.
154 */
155 $query = apply_filters( 'jetpack_sharing_display_query', $query, $this, $id, $args );
156
157 if ( ! empty( $query ) ) {
158 if ( false === stripos( $url, '?' ) ) {
159 $url .= '?' . $query;
160 } else {
161 $url .= '&amp;' . $query;
162 }
163 }
164
165 if ( 'text' == $this->button_style ) {
166 $klasses[] = 'no-icon';
167 }
168
169 /**
170 * Filter the sharing display classes.
171 *
172 * @module sharedaddy
173 *
174 * @since 3.4.0
175 *
176 * @param array $klasses Sharing service classes.
177 * @param object $this Sharing service properties.
178 * @param int|false $id Sharing ID.
179 * @param array $args Array of sharing service options.
180 */
181 $klasses = apply_filters( 'jetpack_sharing_display_classes', $klasses, $this, $id, $args );
182 /**
183 * Filter the sharing display title.
184 *
185 * @module sharedaddy
186 *
187 * @since 3.4.0
188 *
189 * @param string $title Sharing service title.
190 * @param object $this Sharing service properties.
191 * @param int|false $id Sharing ID.
192 * @param array $args Array of sharing service options.
193 */
194 $title = apply_filters( 'jetpack_sharing_display_title', $title, $this, $id, $args );
195 /**
196 * Filter the sharing display text.
197 *
198 * @module sharedaddy
199 *
200 * @since 3.4.0
201 *
202 * @param string $text Sharing service text.
203 * @param object $this Sharing service properties.
204 * @param int|false $id Sharing ID.
205 * @param array $args Array of sharing service options.
206 */
207 $text = apply_filters( 'jetpack_sharing_display_text', $text, $this, $id, $args );
208
209 return sprintf(
210 '<a rel="nofollow%s" data-shared="%s" class="%s" href="%s"%s title="%s"><span%s>%s</span></a>',
211 ( true == $this->open_link_in_new ) ? ' noopener noreferrer' : '',
212 ( $id ? esc_attr( $id ) : '' ),
213 implode( ' ', $klasses ),
214 $url,
215 ( true == $this->open_link_in_new ) ? ' target="_blank"' : '',
216 $title,
217 ( 'icon' == $this->button_style ) ? '></span><span class="sharing-screen-reader-text"' : '',
218 $text
219 );
220 }
221
222 /**
223 * Get an unfiltered post permalink to use when generating a sharing URL with get_link.
224 * Use instead of get_share_url for non-official styles as get_permalink ensures that process_request
225 * will be executed more reliably, in the case that the filtered URL uses a service that strips query parameters.
226 *
227 * @since 3.7.0
228 * @param int $post_id Post ID.
229 * @uses get_permalink
230 * @return string get_permalink( $post_id ) Post permalink.
231 */
232 public function get_process_request_url( $post_id ) {
233 return get_permalink( $post_id );
234 }
235
236 abstract public function get_name();
237 abstract public function get_display( $post );
238
239 public function display_header() {
240 }
241
242 public function display_footer() {
243 }
244
245 public function has_advanced_options() {
246 return false;
247 }
248
249 /**
250 * Get the AMP specific markup for a sharing button.
251 *
252 * @param \WP_Post $post The current post being viewed.
253 */
254 public function get_amp_display( $post ) {
255 // Only display markup if we're on a post.
256 if ( empty( $post ) ) {
257 return false;
258 }
259
260 return $this->build_amp_markup();
261 }
262
263 /**
264 * Generates and returns the markup for an AMP sharing button.
265 *
266 * @param array $attrs Custom attributes for rendering the social icon.
267 */
268 protected function build_amp_markup( $attrs = array() ) {
269
270 $title = sprintf(
271 /* translators: placeholder is a service name, such as "Twitter" or "Facebook". */
272 __( 'Click to share on %s', 'jetpack' ),
273 $this->get_name()
274 );
275
276 $attrs = array_merge(
277 array(
278 'type' => $this->get_id(),
279 'height' => '32px',
280 'width' => '32px',
281 'aria-label' => $title,
282 'title' => $title,
283 ),
284 $attrs
285 );
286 $sharing_link = '<amp-social-share';
287 foreach ( $attrs as $key => $value ) {
288 $sharing_link .= sprintf( ' %s="%s"', sanitize_key( $key ), esc_attr( $value ) );
289 }
290 $sharing_link .= '></amp-social-share>';
291 return $sharing_link;
292 }
293
294 public function display_preview( $echo = true, $force_smart = false, $button_style = null ) {
295 $text = '&nbsp;';
296 $button_style = ( ! empty( $button_style ) ) ? $button_style : $this->button_style;
297 if ( ! $this->smart && ! $force_smart ) {
298 if ( $button_style != 'icon' ) {
299 $text = $this->get_name();
300 }
301 }
302
303 $klasses = array( 'share-' . $this->get_class(), 'sd-button' );
304
305 if ( $button_style == 'icon' || $button_style == 'icon-text' ) {
306 $klasses[] = 'share-icon';
307 }
308
309 if ( $button_style == 'icon' ) {
310 $klasses[] = 'no-text';
311 }
312
313 if ( $button_style == 'text' ) {
314 $klasses[] = 'no-icon';
315 }
316
317 $is_deprecated = $this->is_deprecated();
318
319 $link = sprintf(
320 '<a rel="nofollow" class="%s" href="javascript:void(0)" title="%s"><span>%s</span></a>',
321 implode( ' ', $klasses ),
322 esc_attr(
323 $is_deprecated
324 /* translators: %1$s is the name of a deprecated Sharing Service like "Google+" */
325 ? sprintf( __( 'The %1$s service has shut down. This sharing button is not displayed to your visitors and should be removed.', 'jetpack' ), $this->get_name() )
326 : $this->get_name()
327 ),
328 esc_html(
329 $is_deprecated
330 /* translators: %1$s is the name of a deprecated Sharing Service like "Google+" */
331 ? sprintf( __( '%1$s has shut down', 'jetpack' ), $this->get_name() )
332 : $text
333 )
334 );
335
336 $smart = ( $this->smart || $force_smart ) ? 'on' : 'off';
337 $return = "<div class='option option-smart-$smart'>$link</div>";
338 if ( $echo ) {
339 echo $return;
340 }
341
342 return $return;
343 }
344
345 public function get_total( $post = false ) {
346 global $wpdb, $blog_id;
347
348 $name = strtolower( $this->get_id() );
349
350 if ( $post == false ) {
351 // get total number of shares for service
352 return (int) $wpdb->get_var( $wpdb->prepare( 'SELECT SUM( count ) FROM sharing_stats WHERE blog_id = %d AND share_service = %s', $blog_id, $name ) );
353 }
354
355 // get total shares for a post
356 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 ) );
357 }
358
359 public function get_posts_total() {
360 global $wpdb, $blog_id;
361
362 $totals = array();
363 $name = strtolower( $this->get_id() );
364
365 $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 ) );
366
367 if ( ! empty( $my_data ) ) {
368 foreach ( $my_data as $row ) {
369 $totals[] = new Sharing_Post_Total( $row->id, $row->total );
370 }
371 }
372
373 usort( $totals, array( 'Sharing_Post_Total', 'cmp' ) );
374
375 return $totals;
376 }
377
378 public function process_request( $post, array $post_data ) {
379 /**
380 * Fires when a post is shared via one of the sharing buttons.
381 *
382 * @module sharedaddy
383 *
384 * @since 1.1.0
385 *
386 * @param array $args Aray of information about the sharing service.
387 */
388 do_action( 'sharing_bump_stats', array( 'service' => $this, 'post' => $post ) );
389 }
390
391 public function js_dialog( $name, $params = array() ) {
392 if ( true !== $this->open_link_in_new ) {
393 return;
394 }
395
396 $defaults = array(
397 'menubar' => 1,
398 'resizable' => 1,
399 'width' => 600,
400 'height' => 400,
401 );
402 $params = array_merge( $defaults, $params );
403 $opts = array();
404 foreach ( $params as $key => $val ) {
405 $opts[] = "$key=$val";
406 }
407 $opts = implode( ',', $opts );
408
409 // Add JS after sharing-js has been enqueued.
410 wp_add_inline_script( 'sharing-js',
411 "var windowOpen;
412 jQuery( document.body ).on( 'click', 'a.share-$name', function() {
413 // If there's another sharing window open, close it.
414 if ( 'undefined' !== typeof windowOpen ) {
415 windowOpen.close();
416 }
417 windowOpen = window.open( jQuery( this ).attr( 'href' ), 'wpcom$name', '$opts' );
418 return false;
419 });"
420 );
421 }
422 }
423
424 abstract class Deprecated_Sharing_Source extends Sharing_Source {
425 public $button_style = 'text';
426 public $smart = false;
427 protected $open_link_in_new = false;
428 protected $id;
429 protected $deprecated = true;
430
431 final public function __construct( $id, array $settings ) {
432 $this->id = $id;
433
434 if ( isset( $settings['button_style'] ) ) {
435 $this->button_style = $settings['button_style'];
436 }
437 }
438
439 final public function is_deprecated() {
440 return true;
441 }
442
443 final public function get_share_url( $post_id ) {
444 return get_permalink( $post_id );
445 }
446
447 /**
448 * No AMP display for deprecated sources.
449 *
450 * @param \WP_Post $post The current post being viewed.
451 */
452 final public function get_amp_display( $post ) { // phpcs:ignore VariableAnalysis.CodeAnalysis.VariableAnalysis.UnusedVariable
453 return false;
454 }
455
456 final public function display_preview( $echo = true, $force_smart = false, $button_style = null ) {
457 return parent::display_preview( $echo, false, $button_style );
458 }
459
460 final public function get_total( $post = false ) {
461 return 0;
462 }
463
464 final public function get_posts_total() {
465 return 0;
466 }
467
468 final public function process_request( $post, array $post_data ) {
469 parent::process_request( $post, $post_data );
470 }
471
472 final public function get_display( $post ) {
473 if ( current_user_can( 'manage_options' ) ) {
474 return $this->display_deprecated( $post );
475 }
476
477 return '';
478 }
479
480 public function display_deprecated( $post ) {
481 return $this->get_link(
482 $this->get_share_url( $post->ID ),
483 /* translators: %1$s is the name of a deprecated Sharing Service like "Google+" */
484 sprintf( __( '%1$s has shut down', 'jetpack' ), $this->get_name() ),
485 /* translators: %1$s is the name of a deprecated Sharing Service like "Google+" */
486 sprintf( __( 'The %1$s service has shut down. This sharing button is not displayed to your visitors and should be removed.', 'jetpack' ), $this->get_name() )
487 );
488 }
489 }
490
491 abstract class Sharing_Advanced_Source extends Sharing_Source {
492 public function has_advanced_options() {
493 return true;
494 }
495
496 abstract public function display_options();
497 abstract public function update_options( array $data );
498 abstract public function get_options();
499 }
500
501 class Share_Email extends Sharing_Source {
502 public $shortname = 'email';
503 public $icon = '\f410';
504 public function __construct( $id, array $settings ) {
505 parent::__construct( $id, $settings );
506
507 if ( 'official' == $this->button_style ) {
508 $this->smart = true;
509 } else {
510 $this->smart = false;
511 }
512 }
513
514 public function get_name() {
515 return _x( 'Email', 'as sharing source', 'jetpack' );
516 }
517
518 // Default does nothing
519 public function process_request( $post, array $post_data ) {
520 $ajax = false;
521 if ( isset( $_SERVER['HTTP_X_REQUESTED_WITH'] ) && strtolower( $_SERVER['HTTP_X_REQUESTED_WITH'] ) == 'xmlhttprequest' ) {
522 $ajax = true;
523 }
524
525 $source_email = $target_email = $source_name = false;
526
527 if ( isset( $post_data['source_email'] ) && is_email( $post_data['source_email'] ) ) {
528 $source_email = $post_data['source_email'];
529 }
530
531 if ( isset( $post_data['target_email'] ) && is_email( $post_data['target_email'] ) ) {
532 $target_email = $post_data['target_email'];
533 }
534
535 if ( isset( $post_data['source_name'] ) && strlen( $post_data['source_name'] ) < 200 ) {
536 $source_name = $post_data['source_name'];
537 } elseif ( isset( $post_data['source_name'] ) ) {
538 $source_name = substr( $post_data['source_name'], 0, 200 );
539 } else {
540 $source_name = '';
541 }
542
543 // Test email
544 $error = 1; // Failure in data
545 if ( empty( $post_data['source_f_name'] ) && $source_email && $target_email && $source_name ) {
546 /**
547 * Allow plugins to stop the email sharing button from running the shared message through Akismet.
548 *
549 * @module sharedaddy
550 *
551 * @since 1.1.0
552 *
553 * @param bool true Should we check if the message isn't spam?
554 * @param object $post Post information.
555 * @param array $post_data Information about the shared message.
556 */
557 if ( apply_filters( 'sharing_email_check', true, $post, $post_data ) ) {
558 $data = array(
559 'post' => $post,
560 'source' => $source_email,
561 'target' => $target_email,
562 'name' => $source_name,
563 'sharing_source' => $this,
564 );
565 // todo: implement an error message when email doesn't get sent.
566 /**
567 * Filter whether an email can be sent from the Email sharing button.
568 *
569 * @module sharedaddy
570 *
571 * @since 1.1.0
572 *
573 * @param array $data Array of information about the shared message.
574 */
575 if ( ( $data = apply_filters( 'sharing_email_can_send', $data ) ) !== false ) {
576 // Record stats
577 parent::process_request( $data['post'], $post_data );
578
579 /**
580 * Fires when an email is sent via the Email sharing button.
581 *
582 * @module sharedaddy
583 *
584 * @since 1.1.0
585 *
586 * @param array $data Array of information about the shared message.
587 */
588 do_action( 'sharing_email_send_post', $data );
589 }
590
591 // Return a positive regardless of whether the user is subscribed or not
592 if ( $ajax ) {
593 ?>
594 <div class="response">
595 <div class="response-title"><?php _e( 'This post has been shared!', 'jetpack' ); ?></div>
596 <div class="response-sub"><?php printf( __( 'You have shared this post with %s', 'jetpack' ), esc_html( $target_email ) ); ?></div>
597 <div class="response-close"><a href="#" class="sharing_cancel"><?php _e( 'Close', 'jetpack' ); ?></a></div>
598 </div>
599 <?php
600 } else {
601 wp_safe_redirect( get_permalink( $post->ID ) . '?shared=email' );
602 }
603
604 die();
605 } else {
606 $error = 2; // Email check failed
607 }
608 }
609
610 if ( $ajax ) {
611 echo $error;
612 } else {
613 wp_safe_redirect( get_permalink( $post->ID ) . '?shared=email&msg=fail' );
614 }
615
616 die();
617 }
618
619 public function get_display( $post ) {
620 return $this->get_link( $this->get_process_request_url( $post->ID ), _x( 'Email', 'share to', 'jetpack' ), __( 'Click to email this to a friend', 'jetpack' ), 'share=email' );
621 }
622
623 /**
624 * No AMP display for email.
625 *
626 * @param \WP_Post $post The current post being viewed.
627 */
628 public function get_amp_display( $post ) { // phpcs:ignore
629 return false;
630 }
631
632 /**
633 * Outputs the hidden email dialog
634 */
635 public function display_footer() {
636 global $current_user;
637
638 $visible = $status = false;
639 ?>
640 <div id="sharing_email" style="display: none;">
641 <form action="<?php echo esc_url( $_SERVER['REQUEST_URI'] ); ?>" method="post">
642 <label for="target_email"><?php _e( 'Send to Email Address', 'jetpack' ) ?></label>
643 <input type="email" name="target_email" id="target_email" value="" />
644
645 <?php if ( is_user_logged_in() ) : ?>
646 <input type="hidden" name="source_name" value="<?php echo esc_attr( $current_user->display_name ); ?>" />
647 <input type="hidden" name="source_email" value="<?php echo esc_attr( $current_user->user_email ); ?>" />
648 <?php else : ?>
649
650 <label for="source_name"><?php _e( 'Your Name', 'jetpack' ) ?></label>
651 <input type="text" name="source_name" id="source_name" value="" />
652
653 <label for="source_email"><?php _e( 'Your Email Address', 'jetpack' ) ?></label>
654 <input type="email" name="source_email" id="source_email" value="" />
655
656 <?php endif; ?>
657 <input type="text" id="jetpack-source_f_name" name="source_f_name" class="input" value="" size="25" autocomplete="off" title="<?php esc_attr_e( 'This field is for validation and should not be changed', 'jetpack' ); ?>" />
658 <?php
659 /**
660 * Fires when the Email sharing dialog is loaded.
661 *
662 * @module sharedaddy
663 *
664 * @since 1.1.0
665 *
666 * @param string jetpack Eail sharing source.
667 */
668 do_action( 'sharing_email_dialog', 'jetpack' );
669 ?>
670
671 <img style="float: right; display: none" class="loading" src="<?php
672 /** This filter is documented in modules/stats.php */
673 echo apply_filters( 'jetpack_static_url', plugin_dir_url( __FILE__ ) . 'images/loading.gif' ); ?>" alt="loading" width="16" height="16" />
674 <input type="submit" value="<?php esc_attr_e( 'Send Email', 'jetpack' ); ?>" class="sharing_send" />
675 <a rel="nofollow" href="#cancel" class="sharing_cancel" role="button"><?php _e( 'Cancel', 'jetpack' ); ?></a>
676
677 <div class="errors errors-1" style="display: none;">
678 <?php _e( 'Post was not sent - check your email addresses!', 'jetpack' ); ?>
679 </div>
680
681 <div class="errors errors-2" style="display: none;">
682 <?php _e( 'Email check failed, please try again', 'jetpack' ); ?>
683 </div>
684
685 <div class="errors errors-3" style="display: none;">
686 <?php _e( 'Sorry, your blog cannot share posts by email.', 'jetpack' ); ?>
687 </div>
688 </form>
689 </div>
690 <?php
691 }
692 }
693
694 class Share_Twitter extends Sharing_Source {
695 public $shortname = 'twitter';
696 public $icon = '\f202';
697 // 'https://dev.twitter.com/rest/reference/get/help/configuration' ( 2015/02/06 ) short_url_length is 22, short_url_length_https is 23
698 public $short_url_length = 24;
699
700 public function __construct( $id, array $settings ) {
701 parent::__construct( $id, $settings );
702
703 if ( 'official' == $this->button_style ) {
704 $this->smart = true;
705 } else {
706 $this->smart = false;
707 }
708 }
709
710 public function get_name() {
711 return __( 'Twitter', 'jetpack' );
712 }
713
714 /**
715 * Determine the Twitter 'via' value for a post.
716 *
717 * @param WP_Post|int $post Post object or post ID.
718 * @return string Twitter handle without the preceding @.
719 **/
720 public static function sharing_twitter_via( $post ) {
721 $post = get_post( $post );
722 /**
723 * Allow third-party plugins to customize the Twitter username used as "twitter:site" Twitter Card Meta Tag.
724 *
725 * @module sharedaddy
726 *
727 * @since 3.0.0
728 *
729 * @param string $string Twitter Username.
730 * @param array $args Array of Open Graph Meta Tags and Twitter Cards tags.
731 */
732 $twitter_site_tag_value = apply_filters(
733 'jetpack_twitter_cards_site_tag',
734 '',
735 /** This action is documented in modules/sharedaddy/sharing-sources.php */
736 array( 'twitter:creator' => apply_filters( 'jetpack_sharing_twitter_via', '', $post->ID ) )
737 );
738
739 /*
740 * Hack to remove the unwanted behavior of adding 'via @jetpack' which
741 * was introduced with the adding of the Twitter cards.
742 * This should be a temporary solution until a better method is setup.
743 */
744 if ( 'jetpack' == $twitter_site_tag_value ) {
745 $twitter_site_tag_value = '';
746 }
747
748 /**
749 * Filters the Twitter username used as "via" in the Twitter sharing button.
750 *
751 * @module sharedaddy
752 *
753 * @since 1.7.0
754 *
755 * @param string $twitter_site_tag_value Twitter Username.
756 * @param int $post->ID Post ID.
757 */
758 $twitter_site_tag_value = apply_filters( 'jetpack_sharing_twitter_via', $twitter_site_tag_value, $post->ID );
759
760 // Strip out anything other than a letter, number, or underscore.
761 // This will prevent the inadvertent inclusion of an extra @, as well as normalizing the handle.
762 return preg_replace( '/[^\da-z_]+/i', '', $twitter_site_tag_value );
763 }
764
765 /**
766 * Determine the 'related' Twitter accounts for a post.
767 *
768 * @param WP_Post|int $post Post object or post ID.
769 * @return string Comma-separated list of Twitter handles.
770 **/
771 public static function get_related_accounts( $post ) {
772 $post = get_post( $post );
773 /**
774 * Filter the list of related Twitter accounts added to the Twitter sharing button.
775 *
776 * @module sharedaddy
777 *
778 * @since 1.7.0
779 *
780 * @param array $args Array of Twitter usernames. Format is 'username' => 'Optional description'
781 * @param int $post->ID Post ID.
782 */
783 $related_accounts = apply_filters( 'jetpack_sharing_twitter_related', array(), $post->ID );
784
785 // Example related string: account1,account2:Account 2 description,account3
786 $related = array();
787
788 foreach ( $related_accounts as $related_account_username => $related_account_description ) {
789 // Join the description onto the end of the username
790 if ( $related_account_description ) {
791 $related_account_username .= ':' . $related_account_description;
792 }
793
794 $related[] = $related_account_username;
795 }
796
797 return implode( ',', $related );
798 }
799
800 public function get_display( $post ) {
801 $via = $this->sharing_twitter_via( $post );
802
803 if ( $via ) {
804 $via = 'data-via="' . esc_attr( $via ) . '"';
805 } else {
806 $via = '';
807 }
808
809 $related = $this->get_related_accounts( $post );
810 if ( ! empty( $related ) && $related !== $via ) {
811 $related = 'data-related="' . esc_attr( $related ) . '"';
812 } else {
813 $related = '';
814 }
815
816 if ( $this->smart ) {
817 $share_url = $this->get_share_url( $post->ID );
818 $post_title = $this->get_share_title( $post->ID );
819 return sprintf(
820 '<a href="https://twitter.com/share" class="twitter-share-button" data-url="%1$s" data-text="%2$s" %3$s %4$s>Tweet</a>',
821 esc_url( $share_url ),
822 esc_attr( $post_title ),
823 $via,
824 $related
825 );
826 } else {
827 if (
828 /**
829 * Allow plugins to disable sharing counts for specific sharing services.
830 *
831 * @module sharedaddy
832 *
833 * @since 3.0.0
834 *
835 * @param bool true Should sharing counts be enabled for this specific service. Default to true.
836 * @param int $post->ID Post ID.
837 * @param string $str Sharing service name.
838 */
839 apply_filters( 'jetpack_register_post_for_share_counts', true, $post->ID, 'twitter' )
840 ) {
841 sharing_register_post_for_share_counts( $post->ID );
842 }
843 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 );
844 }
845 }
846
847 public function process_request( $post, array $post_data ) {
848 $post_title = $this->get_share_title( $post->ID );
849 $post_link = $this->get_share_url( $post->ID );
850
851 if ( function_exists( 'mb_stripos' ) ) {
852 $strlen = 'mb_strlen';
853 $substr = 'mb_substr';
854 } else {
855 $strlen = 'strlen';
856 $substr = 'substr';
857 }
858
859 $via = $this->sharing_twitter_via( $post );
860 $related = $this->get_related_accounts( $post );
861 if ( $via ) {
862 $sig = " via @$via";
863 if ( $related === $via ) {
864 $related = false;
865 }
866 } else {
867 $via = false;
868 $sig = '';
869 }
870
871 $suffix_length = $this->short_url_length + $strlen( $sig );
872 // $sig is handled by twitter in their 'via' argument.
873 // $post_link is handled by twitter in their 'url' argument.
874 if ( 280 < $strlen( $post_title ) + $suffix_length ) {
875 // The -1 is for "\xE2\x80\xA6", a UTF-8 ellipsis.
876 $text = $substr( $post_title, 0, 280 - $suffix_length - 1 ) . "\xE2\x80\xA6";
877 } else {
878 $text = $post_title;
879 }
880
881 // Record stats
882 parent::process_request( $post, $post_data );
883
884 $url = $post_link;
885 $twitter_url = add_query_arg(
886 rawurlencode_deep( array_filter( compact( 'via', 'related', 'text', 'url' ) ) ),
887 'https://twitter.com/intent/tweet'
888 );
889
890 // Redirect to Twitter
891 wp_redirect( $twitter_url );
892 die();
893 }
894
895 public function has_custom_button_style() {
896 return $this->smart;
897 }
898
899 public function display_footer() {
900 if ( $this->smart ) {
901 ?>
902 <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>
903 <?php
904 } else {
905 $this->js_dialog( $this->shortname, array( 'height' => 350 ) );
906 }
907 }
908 }
909
910
911 class Share_Reddit extends Sharing_Source {
912 public $shortname = 'reddit';
913 public $icon = '\f222';
914 public function __construct( $id, array $settings ) {
915 parent::__construct( $id, $settings );
916
917 if ( 'official' == $this->button_style ) {
918 $this->smart = true;
919 } else {
920 $this->smart = false;
921 }
922 }
923
924 public function get_name() {
925 return __( 'Reddit', 'jetpack' );
926 }
927
928 public function get_display( $post ) {
929 if ( $this->smart ) {
930 return '<div class="reddit_button"><iframe src="' . $this->http() . '://www.reddit.com/static/button/button1.html?newwindow=true&width=120&amp;url=' . rawurlencode( $this->get_share_url( $post->ID ) ) . '&amp;title=' . rawurlencode( $this->get_share_title( $post->ID ) ) . '" height="22" width="120" scrolling="no" frameborder="0"></iframe></div>';
931 } else {
932 return $this->get_link( $this->get_process_request_url( $post->ID ), _x( 'Reddit', 'share to', 'jetpack' ), __( 'Click to share on Reddit', 'jetpack' ), 'share=reddit' );
933 }
934 }
935
936 /**
937 * AMP display for Reddit.
938 *
939 * @param \WP_Post $post The current post being viewed.
940 */
941 public function get_amp_display( $post ) {
942 $attrs = array(
943 '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 ) ) ),
944 );
945
946 return $this->build_amp_markup( $attrs );
947 }
948
949 public function process_request( $post, array $post_data ) {
950 $reddit_url = $this->http() . '://reddit.com/submit?url=' . rawurlencode( $this->get_share_url( $post->ID ) ) . '&title=' . rawurlencode( $this->get_share_title( $post->ID ) );
951
952 // Record stats
953 parent::process_request( $post, $post_data );
954
955 // Redirect to Reddit
956 wp_redirect( $reddit_url );
957 die();
958 }
959 }
960
961 class Share_LinkedIn extends Sharing_Source {
962 public $shortname = 'linkedin';
963 public $icon = '\f207';
964 public function __construct( $id, array $settings ) {
965 parent::__construct( $id, $settings );
966
967 if ( 'official' == $this->button_style ) {
968 $this->smart = true;
969 } else {
970 $this->smart = false;
971 }
972 }
973
974 public function get_name() {
975 return __( 'LinkedIn', 'jetpack' );
976 }
977
978 public function has_custom_button_style() {
979 return $this->smart;
980 }
981
982 public function get_display( $post ) {
983 $display = '';
984
985 if ( $this->smart ) {
986 $share_url = $this->get_share_url( $post->ID );
987 $display .= sprintf( '<div class="linkedin_button"><script type="in/share" data-url="%s" data-counter="right"></script></div>', esc_url( $share_url ) );
988 } else {
989 $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 );
990 }
991
992 /** This filter is already documented in modules/sharedaddy/sharing-sources.php */
993 if ( apply_filters( 'jetpack_register_post_for_share_counts', true, $post->ID, 'linkedin' ) ) {
994 sharing_register_post_for_share_counts( $post->ID );
995 }
996
997 return $display;
998 }
999
1000 public function process_request( $post, array $post_data ) {
1001
1002 $post_link = $this->get_share_url( $post->ID );
1003
1004 // Using the same URL as the official button, which is *not* LinkedIn's documented sharing link
1005 // https://www.linkedin.com/cws/share?url={url}&token=&isFramed=false
1006 $linkedin_url = add_query_arg( array(
1007 'url' => rawurlencode( $post_link ),
1008 ), 'https://www.linkedin.com/cws/share?token=&isFramed=false' );
1009
1010 // Record stats
1011 parent::process_request( $post, $post_data );
1012
1013 // Redirect to LinkedIn
1014 wp_redirect( $linkedin_url );
1015 die();
1016 }
1017
1018 public function display_footer() {
1019 if ( ! $this->smart ) {
1020 $this->js_dialog( $this->shortname, array( 'width' => 580, 'height' => 450 ) );
1021 } else {
1022 ?><script type="text/javascript">
1023 jQuery( document ).ready( function() {
1024 jQuery.getScript( 'https://platform.linkedin.com/in.js?async=true', function success() {
1025 IN.init();
1026 });
1027 });
1028 jQuery( document.body ).on( 'post-load', function() {
1029 if ( typeof IN != 'undefined' )
1030 IN.parse();
1031 });
1032 </script><?php
1033 }
1034 }
1035 }
1036
1037 class Share_Facebook extends Sharing_Source {
1038 public $shortname = 'facebook';
1039 public $icon = '\f204';
1040 private $share_type = 'default';
1041
1042 public function __construct( $id, array $settings ) {
1043 parent::__construct( $id, $settings );
1044
1045 if ( isset( $settings['share_type'] ) ) {
1046 $this->share_type = $settings['share_type'];
1047 }
1048
1049 if ( 'official' == $this->button_style ) {
1050 $this->smart = true;
1051 } else {
1052 $this->smart = false;
1053 }
1054 }
1055
1056 public function get_name() {
1057 return __( 'Facebook', 'jetpack' );
1058 }
1059
1060 public function display_header() {
1061 }
1062
1063 function guess_locale_from_lang( $lang ) {
1064 if ( 'en' == $lang || 'en_US' == $lang || ! $lang ) {
1065 return 'en_US';
1066 }
1067
1068 if ( ! class_exists( 'GP_Locales' ) ) {
1069 if ( ! defined( 'JETPACK__GLOTPRESS_LOCALES_PATH' ) || ! file_exists( JETPACK__GLOTPRESS_LOCALES_PATH ) ) {
1070 return false;
1071 }
1072
1073 require JETPACK__GLOTPRESS_LOCALES_PATH;
1074 }
1075
1076 if ( defined( 'IS_WPCOM' ) && IS_WPCOM ) {
1077 // WP.com: get_locale() returns 'it'
1078 $locale = GP_Locales::by_slug( $lang );
1079 } else {
1080 // Jetpack: get_locale() returns 'it_IT';
1081 $locale = GP_Locales::by_field( 'wp_locale', $lang );
1082 }
1083
1084 if ( ! $locale ) {
1085 return false;
1086 }
1087
1088 if ( empty( $locale->facebook_locale ) ) {
1089 if ( empty( $locale->wp_locale ) ) {
1090 return false;
1091 } else {
1092 // Facebook SDK is smart enough to fall back to en_US if a
1093 // locale isn't supported. Since supported Facebook locales
1094 // can fall out of sync, we'll attempt to use the known
1095 // wp_locale value and rely on said fallback.
1096 return $locale->wp_locale;
1097 }
1098 }
1099
1100 return $locale->facebook_locale;
1101 }
1102
1103 public function get_display( $post ) {
1104 if ( $this->smart ) {
1105 $share_url = $this->get_share_url( $post->ID );
1106 $fb_share_html = '<div class="fb-share-button" data-href="' . esc_attr( $share_url ) . '" data-layout="button_count"></div>';
1107 /**
1108 * Filter the output of the Facebook Sharing button.
1109 *
1110 * @module sharedaddy
1111 *
1112 * @since 3.6.0
1113 *
1114 * @param string $fb_share_html Facebook Sharing button HTML.
1115 * @param string $share_url URL of the post to share.
1116 */
1117 return apply_filters( 'jetpack_sharing_facebook_official_button_output', $fb_share_html, $share_url );
1118 }
1119
1120 /** This filter is already documented in modules/sharedaddy/sharing-sources.php */
1121 if ( apply_filters( 'jetpack_register_post_for_share_counts', true, $post->ID, 'facebook' ) ) {
1122 sharing_register_post_for_share_counts( $post->ID );
1123 }
1124 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 );
1125 }
1126
1127 /**
1128 * AMP display for Facebook.
1129 *
1130 * @param \WP_Post $post The current post being viewed.
1131 */
1132 public function get_amp_display( $post ) {
1133 $attrs = array(
1134 /** This filter is documented in modules/sharedaddy/sharing-sources.php */
1135 'data-param-app_id' => apply_filters( 'jetpack_sharing_facebook_app_id', '249643311490' ),
1136 );
1137
1138 return $this->build_amp_markup( $attrs );
1139 }
1140
1141 public function process_request( $post, array $post_data ) {
1142 $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 ) );
1143
1144 // Record stats
1145 parent::process_request( $post, $post_data );
1146
1147 // Redirect to Facebook
1148 wp_redirect( $fb_url );
1149 die();
1150 }
1151
1152 public function display_footer() {
1153 $this->js_dialog( $this->shortname );
1154 if ( $this->smart ) {
1155 $locale = $this->guess_locale_from_lang( get_locale() );
1156 if ( ! $locale ) {
1157 $locale = 'en_US';
1158 }
1159 /**
1160 * Filter the App ID used in the official Facebook Share button.
1161 *
1162 * @since 3.8.0
1163 *
1164 * @param int $fb_app_id Facebook App ID. Default to 249643311490 (WordPress.com's App ID).
1165 */
1166 $fb_app_id = apply_filters( 'jetpack_sharing_facebook_app_id', '249643311490' );
1167 if ( is_numeric( $fb_app_id ) ) {
1168 $fb_app_id = '&appId=' . $fb_app_id;
1169 } else {
1170 $fb_app_id = '';
1171 }
1172 ?><div id="fb-root"></div>
1173 <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 $locale; ?>/sdk.js#xfbml=1<?php echo $fb_app_id; ?>&version=v2.3'; fjs.parentNode.insertBefore(js, fjs); }(document, 'script', 'facebook-jssdk'));</script>
1174 <script>
1175 document.body.addEventListener( 'post-load', function() {
1176 if ( 'undefined' !== typeof FB ) {
1177 FB.XFBML.parse();
1178 }
1179 } );
1180 </script>
1181 <?php
1182 }
1183 }
1184 }
1185
1186 class Share_Print extends Sharing_Source {
1187 public $shortname = 'print';
1188 public $icon = '\f469';
1189 public function __construct( $id, array $settings ) {
1190 parent::__construct( $id, $settings );
1191
1192 if ( 'official' == $this->button_style ) {
1193 $this->smart = true;
1194 } else {
1195 $this->smart = false;
1196 }
1197 }
1198
1199 public function get_name() {
1200 return __( 'Print', 'jetpack' );
1201 }
1202
1203 public function get_display( $post ) {
1204 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' ) );
1205 }
1206
1207 /**
1208 * AMP display for Print.
1209 *
1210 * @param \WP_Post $post The current post being viewed.
1211 */
1212 public function get_amp_display( $post ) {
1213 if ( empty( $post ) ) {
1214 return false;
1215 }
1216
1217 return '<button class="amp-social-share print" on="tap:AMP.print">Print</button>';
1218 }
1219 }
1220
1221 class Share_PressThis extends Sharing_Source {
1222 public $shortname = 'pressthis';
1223 public $icon = '\f205';
1224 public function __construct( $id, array $settings ) {
1225 parent::__construct( $id, $settings );
1226
1227 if ( 'official' == $this->button_style ) {
1228 $this->smart = true;
1229 } else {
1230 $this->smart = false;
1231 }
1232 }
1233
1234 public function get_name() {
1235 return __( 'Press This', 'jetpack' );
1236 }
1237
1238 public function process_request( $post, array $post_data ) {
1239 global $current_user;
1240
1241 $primary_blog = (int) get_user_meta( $current_user->ID, 'primary_blog', true );
1242 if ( $primary_blog ) {
1243 $primary_blog_details = get_blog_details( $primary_blog );
1244 } else {
1245 $primary_blog_details = false;
1246 }
1247
1248 if ( $primary_blog_details ) {
1249 $blogs = array( $primary_blog_details );
1250 } elseif ( function_exists( 'get_active_blogs_for_user' ) ) {
1251 $blogs = get_active_blogs_for_user();
1252 if ( empty( $blogs ) ) {
1253 $blogs = get_blogs_of_user( $current_user->ID );
1254 }
1255 } else {
1256 $blogs = get_blogs_of_user( $current_user->ID );
1257 }
1258
1259 if ( empty( $blogs ) ) {
1260 wp_safe_redirect( get_permalink( $post->ID ) );
1261 die();
1262 }
1263
1264 $blog = current( $blogs );
1265
1266 $args = array(
1267 'u' => rawurlencode( $this->get_share_url( $post->ID ) ),
1268 );
1269
1270 $args[ 'url-scan-submit' ] = 'Scan';
1271 $args[ '_wpnonce' ] = wp_create_nonce( 'scan-site' );
1272
1273 $url = $blog->siteurl . '/wp-admin/press-this.php';
1274 $url = add_query_arg( $args, $url );
1275
1276 // Record stats
1277 parent::process_request( $post, $post_data );
1278
1279 // Redirect to Press This
1280 wp_redirect( $url );
1281 die();
1282 }
1283
1284 public function get_display( $post ) {
1285 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' );
1286 }
1287
1288 /**
1289 * No AMP display for PressThis.
1290 *
1291 * @param \WP_Post $post The current post being viewed.
1292 */
1293 public function get_amp_display( $post ) { // phpcs:ignore
1294 return false;
1295 }
1296 }
1297
1298 class Share_Custom extends Sharing_Advanced_Source {
1299 private $name;
1300 private $icon;
1301 private $url;
1302 public $smart = true;
1303 public $shortname;
1304
1305 public function get_class() {
1306 return 'custom share-custom-' . sanitize_html_class( strtolower( $this->name ) );
1307 }
1308
1309 public function __construct( $id, array $settings ) {
1310 parent::__construct( $id, $settings );
1311
1312 $opts = $this->get_options();
1313
1314 if ( isset( $settings['name'] ) ) {
1315 $this->name = $settings['name'];
1316 $this->shortname = preg_replace( '/[^a-z0-9]*/', '', $settings['name'] );
1317 }
1318
1319 if ( isset( $settings['icon'] ) ) {
1320 $this->icon = $settings['icon'];
1321
1322 $new_icon = esc_url_raw( wp_specialchars_decode( $this->icon, ENT_QUOTES ) );
1323 $i = 0;
1324 while ( $new_icon != $this->icon ) {
1325 if ( $i > 5 ) {
1326 $this->icon = false;
1327 break;
1328 } else {
1329 $this->icon = $new_icon;
1330 $new_icon = esc_url_raw( wp_specialchars_decode( $this->icon, ENT_QUOTES ) );
1331 }
1332 $i++;
1333 }
1334 }
1335
1336 if ( isset( $settings['url'] ) ) {
1337 $this->url = $settings['url'];
1338 }
1339 }
1340
1341 public function get_name() {
1342 return $this->name;
1343 }
1344
1345 public function get_display( $post ) {
1346 $str = $this->get_link( $this->get_process_request_url( $post->ID ), esc_html( $this->name ), sprintf( __( 'Click to share on %s', 'jetpack' ), esc_attr( $this->name ) ), 'share=' . $this->id );
1347 return str_replace( '<span>', '<span style="' . esc_attr( 'background-image:url("' . addcslashes( esc_url_raw( $this->icon ), '"' ) . '");' ) . '">', $str );
1348 }
1349
1350 /**
1351 * No AMP display for custom elements.
1352 *
1353 * @param \WP_Post $post The current post being viewed.
1354 */
1355 public function get_amp_display( $post ) { // phpcs:ignore
1356 return false;
1357 }
1358
1359 public function process_request( $post, array $post_data ) {
1360 $url = str_replace( '&amp;', '&', $this->url );
1361 $url = str_replace( '%post_id%', rawurlencode( $post->ID ), $url );
1362 $url = str_replace( '%post_url%', rawurlencode( $this->get_share_url( $post->ID ) ), $url );
1363 $url = str_replace( '%post_full_url%', rawurlencode( get_permalink( $post->ID ) ), $url );
1364 $url = str_replace( '%post_title%', rawurlencode( $this->get_share_title( $post->ID ) ), $url );
1365 $url = str_replace( '%home_url%', rawurlencode( home_url() ), $url );
1366 $url = str_replace( '%post_slug%', rawurlencode( $post->post_name ), $url );
1367
1368 if ( strpos( $url, '%post_tags%' ) !== false ) {
1369 $tags = get_the_tags( $post->ID );
1370 $tagged = '';
1371
1372 if ( $tags ) {
1373 $tagged_raw = array();
1374 foreach ( $tags as $tag ) {
1375 $tagged_raw[] = rawurlencode( $tag->name );
1376 }
1377
1378 $tagged = implode( ',', $tagged_raw );
1379 }
1380
1381 $url = str_replace( '%post_tags%', $tagged, $url );
1382 }
1383
1384 if ( strpos( $url, '%post_excerpt%' ) !== false ) {
1385 $url_excerpt = $post->post_excerpt;
1386 if ( empty( $url_excerpt ) ) {
1387 $url_excerpt = $post->post_content;
1388 }
1389
1390 $url_excerpt = strip_tags( strip_shortcodes( $url_excerpt ) );
1391 $url_excerpt = wp_html_excerpt( $url_excerpt, 100 );
1392 $url_excerpt = rtrim( preg_replace( '/[^ .]*$/', '', $url_excerpt ) );
1393 $url = str_replace( '%post_excerpt%', rawurlencode( $url_excerpt ), $url );
1394 }
1395
1396 // Record stats
1397 parent::process_request( $post, $post_data );
1398
1399 // Redirect
1400 wp_redirect( $url );
1401 die();
1402 }
1403
1404 public function display_options() {
1405 ?>
1406 <div class="input">
1407 <table class="form-table">
1408 <tbody>
1409 <tr>
1410 <th scope="row"><?php _e( 'Label', 'jetpack' ); ?></th>
1411 <td><input type="text" name="name" value="<?php echo esc_attr( $this->name ); ?>" /></td>
1412 </tr>
1413
1414 <tr>
1415 <th scope="row"><?php _e( 'URL', 'jetpack' ); ?></th>
1416 <td><input type="text" name="url" value="<?php echo esc_attr( $this->url ); ?>" /></td>
1417 </tr>
1418
1419 <tr>
1420 <th scope="row"><?php _e( 'Icon', 'jetpack' ); ?></th>
1421 <td><input type="text" name="icon" value="<?php echo esc_attr( $this->icon ); ?>" /></td>
1422 </tr>
1423
1424 <tr>
1425 <th scope="row"></th>
1426 <td>
1427 <input class="button-secondary" type="submit" value="<?php esc_attr_e( 'Save', 'jetpack' ); ?>" />
1428 <a href="#" class="remove"><small><?php _e( 'Remove Service', 'jetpack' ); ?></small></a>
1429 </td>
1430 </tr>
1431 </tbody>
1432 </table>
1433 </div>
1434 <?php
1435 }
1436
1437 public function update_options( array $data ) {
1438 $name = trim( wp_html_excerpt( wp_kses( stripslashes( $data['name'] ), array() ), 30 ) );
1439 $url = trim( esc_url_raw( $data['url'] ) );
1440 $icon = trim( esc_url_raw( $data['icon'] ) );
1441
1442 if ( $name ) {
1443 $this->name = $name;
1444 }
1445
1446 if ( $url ) {
1447 $this->url = $url;
1448 }
1449
1450 if ( $icon ) {
1451 $this->icon = $icon;
1452 }
1453 }
1454
1455 public function get_options() {
1456 return array(
1457 'name' => $this->name,
1458 'icon' => $this->icon,
1459 'url' => $this->url,
1460 );
1461 }
1462
1463 public function display_preview( $echo = true, $force_smart = false, $button_style = null ) {
1464 $opts = $this->get_options();
1465
1466 $text = '&nbsp;';
1467 if ( ! $this->smart ) {
1468 if ( $this->button_style != 'icon' ) {
1469 $text = $this->get_name();
1470 }
1471 }
1472
1473 $klasses = array( 'share-' . $this->shortname );
1474
1475 if ( $this->button_style == 'icon' || $this->button_style == 'icon-text' ) {
1476 $klasses[] = 'share-icon';
1477 }
1478
1479 if ( $this->button_style == 'icon' ) {
1480 $text = '';
1481 $klasses[] = 'no-text';
1482 }
1483
1484 if ( $this->button_style == 'text' ) {
1485 $klasses[] = 'no-icon';
1486 }
1487
1488 $link = sprintf(
1489 '<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>',
1490 implode( ' ', $klasses ),
1491 $this->get_name(),
1492 addcslashes( esc_url_raw( $opts['icon'] ), '"' ),
1493 $text
1494 );
1495 ?>
1496 <div class="option option-smart-off">
1497 <?php echo $link ; ?>
1498 </div><?php
1499 }
1500 }
1501
1502 class Share_Tumblr extends Sharing_Source {
1503 public $shortname = 'tumblr';
1504 public $icon = '\f214';
1505 public function __construct( $id, array $settings ) {
1506 parent::__construct( $id, $settings );
1507 if ( 'official' == $this->button_style ) {
1508 $this->smart = true;
1509 } else {
1510 $this->smart = false;
1511 }
1512 }
1513
1514 public function get_name() {
1515 return __( 'Tumblr', 'jetpack' );
1516 }
1517
1518 public function get_display( $post ) {
1519 if ( $this->smart ) {
1520 $target = '';
1521 if ( true == $this->open_link_in_new ) {
1522 $target = '_blank';
1523 }
1524
1525 /**
1526 * If we are looking at a single post, let Tumblr figure out the post type (text, photo, link, quote, chat, or video)
1527 * based on the content available on the page.
1528 * If we are not looking at a single post, content from other posts can appear on the page and Tumblr will pick that up.
1529 * 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.
1530 */
1531 if ( ! is_single() ) {
1532 $posttype = 'data-posttype="link"';
1533 } else {
1534 $posttype = '';
1535 }
1536
1537 // Documentation: https://www.tumblr.com/docs/en/share_button
1538 return sprintf(
1539 '<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>',
1540 $target,
1541 'https://www.tumblr.com/share',
1542 $this->get_share_title( $post->ID ),
1543 $this->get_share_url( $post->ID ),
1544 __( 'Share on Tumblr', 'jetpack' ),
1545 $posttype
1546 );
1547 } else {
1548 return $this->get_link( $this->get_process_request_url( $post->ID ), _x( 'Tumblr', 'share to', 'jetpack' ), __( 'Click to share on Tumblr', 'jetpack' ), 'share=tumblr' );
1549 }
1550 }
1551
1552 public function process_request( $post, array $post_data ) {
1553 // Record stats
1554 parent::process_request( $post, $post_data );
1555
1556 // Redirect to Tumblr's sharing endpoint (a la their bookmarklet)
1557 $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=';
1558 wp_redirect( $url );
1559 die();
1560 }
1561
1562 public function display_footer() {
1563 if ( $this->smart ) {
1564 ?><script id="tumblr-js" type="text/javascript" src="https://assets.tumblr.com/share-button.js"></script><?php
1565 } else {
1566 $this->js_dialog( $this->shortname, array( 'width' => 450, 'height' => 450 ) );
1567 }
1568 }
1569 }
1570
1571 class Share_Pinterest extends Sharing_Source {
1572 public $shortname = 'pinterest';
1573 public $icon = '\f209';
1574
1575 public function __construct( $id, array $settings ) {
1576 parent::__construct( $id, $settings );
1577 if ( 'official' == $this->button_style ) {
1578 $this->smart = true;
1579 } else {
1580 $this->smart = false;
1581 }
1582 }
1583
1584 public function get_name() {
1585 return __( 'Pinterest', 'jetpack' );
1586 }
1587
1588 public function get_image( $post ) {
1589 if ( class_exists( 'Jetpack_PostImages' ) ) {
1590 $image = Jetpack_PostImages::get_image( $post->ID, array( 'fallback_to_avatars' => true ) );
1591 if ( ! empty( $image ) ) {
1592 return $image['src'];
1593 }
1594 }
1595
1596 /**
1597 * Filters the default image used by the Pinterest Pin It share button.
1598 *
1599 * @module sharedaddy
1600 *
1601 * @since 3.6.0
1602 *
1603 * @param string $url Default image URL.
1604 */
1605 return apply_filters( 'jetpack_sharing_pinterest_default_image', 'https://s0.wp.com/i/blank.jpg' );
1606 }
1607
1608 public function get_external_url( $post ) {
1609 $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 );
1610
1611 /**
1612 * Filters the Pinterest share URL used in sharing button output.
1613 *
1614 * @module sharedaddy
1615 *
1616 * @since 3.6.0
1617 *
1618 * @param string $url Pinterest share URL.
1619 */
1620 return apply_filters( 'jetpack_sharing_pinterest_share_url', $url );
1621 }
1622
1623 public function get_widget_type() {
1624 /**
1625 * Filters the Pinterest widget type.
1626 *
1627 * @see https://business.pinterest.com/en/widget-builder
1628 *
1629 * @module sharedaddy
1630 *
1631 * @since 3.6.0
1632 *
1633 * @param string $type Pinterest widget type. Default of 'buttonPin' for single-image selection. 'buttonBookmark' for multi-image modal.
1634 */
1635 return apply_filters( 'jetpack_sharing_pinterest_widget_type', 'buttonPin' );
1636 }
1637
1638 public function get_display( $post ) {
1639 $display = '';
1640
1641 if ( $this->smart ) {
1642 $display = sprintf(
1643 '<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>',
1644 esc_url( $this->get_external_url( $post ) ),
1645 esc_attr( $this->get_widget_type() )
1646 );
1647 } else {
1648 $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 );
1649 }
1650
1651 /** This filter is already documented in modules/sharedaddy/sharing-sources.php */
1652 if ( apply_filters( 'jetpack_register_post_for_share_counts', true, $post->ID, 'linkedin' ) ) {
1653 sharing_register_post_for_share_counts( $post->ID );
1654 }
1655
1656 return $display;
1657 }
1658
1659 public function process_request( $post, array $post_data ) {
1660 // Record stats
1661 parent::process_request( $post, $post_data );
1662 // If we're triggering the multi-select panel, then we don't need to redirect to Pinterest
1663 if ( ! isset( $_GET['js_only'] ) ) {
1664 $pinterest_url = esc_url_raw( $this->get_external_url( $post ) );
1665 wp_redirect( $pinterest_url );
1666 } else {
1667 echo '// share count bumped';
1668 }
1669 die();
1670 }
1671
1672 public function display_footer() {
1673 /**
1674 * Filter the Pin it button appearing when hovering over images when using the official button style.
1675 *
1676 * @module sharedaddy
1677 *
1678 * @since 3.6.0
1679 *
1680 * @param bool $jetpack_pinit_over True by default, displays the Pin it button when hovering over images.
1681 */
1682 $jetpack_pinit_over = apply_filters( 'jetpack_pinit_over_button', true );
1683 ?>
1684 <?php if ( $this->smart ) : ?>
1685 <script type="text/javascript">
1686 // Pinterest shared resources
1687 var s = document.createElement("script");
1688 s.type = "text/javascript";
1689 s.async = true;
1690 <?php if ( $jetpack_pinit_over ) {
1691 echo "s.setAttribute('data-pin-hover', true);";
1692 } ?>
1693 s.src = window.location.protocol + "//assets.pinterest.com/js/pinit.js";
1694 var x = document.getElementsByTagName("script")[0];
1695 x.parentNode.insertBefore(s, x);
1696 // if 'Pin it' button has 'counts' make container wider
1697 jQuery(window).load( function(){ jQuery( 'li.share-pinterest a span:visible' ).closest( '.share-pinterest' ).width( '80px' ); } );
1698 </script>
1699 <?php elseif ( 'buttonPin' != $this->get_widget_type() ) : ?>
1700 <script type="text/javascript">
1701 jQuery(document).ready( function(){
1702 jQuery('body').on('click', 'a.share-pinterest', function(e){
1703 e.preventDefault();
1704 // Load Pinterest Bookmarklet code
1705 var s = document.createElement("script");
1706 s.type = "text/javascript";
1707 s.src = window.location.protocol + "//assets.pinterest.com/js/pinmarklet.js?r=" + ( Math.random() * 99999999 );
1708 var x = document.getElementsByTagName("script")[0];
1709 x.parentNode.insertBefore(s, x);
1710 // Trigger Stats
1711 var s = document.createElement("script");
1712 s.type = "text/javascript";
1713 s.src = this + ( this.toString().indexOf( '?' ) ? '&' : '?' ) + 'js_only=1';
1714 var x = document.getElementsByTagName("script")[0];
1715 x.parentNode.insertBefore(s, x);
1716 });
1717 });
1718 </script>
1719 <?php endif;
1720 }
1721 }
1722
1723 class Share_Pocket extends Sharing_Source {
1724 public $shortname = 'pocket';
1725 public $icon = '\f224';
1726
1727 public function __construct( $id, array $settings ) {
1728 parent::__construct( $id, $settings );
1729
1730 if ( 'official' == $this->button_style ) {
1731 $this->smart = true;
1732 } else {
1733 $this->smart = false;
1734 }
1735 }
1736
1737 public function get_name() {
1738 return __( 'Pocket', 'jetpack' );
1739 }
1740
1741 public function process_request( $post, array $post_data ) {
1742 // Record stats
1743 parent::process_request( $post, $post_data );
1744
1745 $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 ) ) );
1746 wp_redirect( $pocket_url );
1747 exit;
1748 }
1749
1750 public function get_display( $post ) {
1751 if ( $this->smart ) {
1752 $post_count = 'horizontal';
1753
1754 $button = '';
1755 $button .= '<div class="pocket_button">';
1756 $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' ) );
1757 $button .= '</div>';
1758
1759 return $button;
1760 } else {
1761 return $this->get_link( $this->get_process_request_url( $post->ID ), _x( 'Pocket', 'share to', 'jetpack' ), __( 'Click to share on Pocket', 'jetpack' ), 'share=pocket' );
1762 }
1763
1764 }
1765
1766 /**
1767 * AMP display for Pocket.
1768 *
1769 * @param \WP_Post $post The current post being viewed.
1770 */
1771 public function get_amp_display( $post ) {
1772 $attrs = array(
1773 '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 ) ) ),
1774 );
1775
1776 return $this->build_amp_markup( $attrs );
1777 }
1778
1779 function display_footer() {
1780 if ( $this->smart ) :
1781 ?>
1782 <script>
1783 // Don't use Pocket's default JS as it we need to force init new Pocket share buttons loaded via JS.
1784 function jetpack_sharing_pocket_init() {
1785 jQuery.getScript( 'https://widgets.getpocket.com/v1/j/btn.js?v=1' );
1786 }
1787 jQuery( document ).ready( jetpack_sharing_pocket_init );
1788 jQuery( document.body ).on( 'post-load', jetpack_sharing_pocket_init );
1789 </script>
1790 <?php
1791 else :
1792 $this->js_dialog( $this->shortname, array( 'width' => 450, 'height' => 450 ) );
1793 endif;
1794
1795 }
1796
1797 }
1798
1799 class Share_Telegram extends Sharing_Source {
1800 public $shortname = 'telegram';
1801
1802 public function __construct( $id, array $settings ) {
1803 parent::__construct( $id, $settings );
1804 }
1805
1806 public function get_name() {
1807 return __( 'Telegram', 'jetpack' );
1808 }
1809 public function process_request( $post, array $post_data ) {
1810 // Record stats
1811 parent::process_request( $post, $post_data );
1812 $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 ) ) );
1813 wp_redirect( $telegram_url );
1814 exit;
1815 }
1816
1817 public function get_display( $post ) {
1818 return $this->get_link( $this->get_process_request_url( $post->ID ), _x( 'Telegram', 'share to', 'jetpack' ), __( 'Click to share on Telegram', 'jetpack' ), 'share=telegram' );
1819 }
1820
1821 /**
1822 * AMP display for Telegram.
1823 *
1824 * @param \WP_Post $post The current post being viewed.
1825 */
1826 public function get_amp_display( $post ) {
1827 $attrs = array(
1828 '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 ) ) ),
1829 );
1830
1831 return $this->build_amp_markup( $attrs );
1832 }
1833
1834 function display_footer() {
1835 $this->js_dialog( $this->shortname, array( 'width' => 450, 'height' => 450 ) );
1836 }
1837 }
1838
1839 class Jetpack_Share_WhatsApp extends Sharing_Source {
1840 public $shortname = 'jetpack-whatsapp';
1841
1842 public function __construct( $id, array $settings ) {
1843 parent::__construct( $id, $settings );
1844 }
1845
1846 public function get_name() {
1847 return __( 'WhatsApp', 'jetpack' );
1848 }
1849
1850 public function get_display( $post ) {
1851 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' );
1852 }
1853
1854 /**
1855 * AMP display for Whatsapp.
1856 *
1857 * @param \WP_Post $post The current post being viewed.
1858 */
1859 public function get_amp_display( $post ) {
1860 $attrs = array(
1861 'type' => 'whatsapp',
1862 );
1863
1864 return $this->build_amp_markup( $attrs );
1865 }
1866
1867 public function process_request( $post, array $post_data ) {
1868 // Record stats
1869 parent::process_request( $post, $post_data );
1870
1871 // Firefox for desktop doesn't handle the "api.whatsapp.com" URL properly, so use "web.whatsapp.com"
1872 if ( User_Agent_Info::is_firefox_desktop() ) {
1873 $url = 'https://web.whatsapp.com/send?text=';
1874 } else {
1875 $url = 'https://api.whatsapp.com/send?text=';
1876 }
1877
1878 $url .= rawurlencode( $this->get_share_title( $post->ID ) . ' ' . $this->get_share_url( $post->ID ) );
1879 wp_redirect( $url );
1880 exit;
1881 }
1882 }
1883
1884 class Share_Skype extends Sharing_Source {
1885 public $shortname = 'skype';
1886 public $icon = '\f220';
1887 private $share_type = 'default';
1888
1889 public function __construct( $id, array $settings ) {
1890 parent::__construct( $id, $settings );
1891
1892 if ( isset( $settings['share_type'] ) ) {
1893 $this->share_type = $settings['share_type'];
1894 }
1895
1896 if ( 'official' == $this->button_style ) {
1897 $this->smart = true;
1898 } else {
1899 $this->smart = false;
1900 }
1901
1902 }
1903
1904 public function get_name() {
1905 return __( 'Skype', 'jetpack' );
1906 }
1907
1908 public function get_display( $post ) {
1909 if ( $this->smart ) {
1910 $skype_share_html = sprintf(
1911 '<div class="skype-share" data-href="%1$s" data-lang="%2$s" data-style="small" data-source="jetpack" ></div>',
1912 esc_attr( $this->get_share_url( $post->ID ) ),
1913 'en-US'
1914 );
1915 return $skype_share_html;
1916 }
1917
1918 /** This filter is already documented in modules/sharedaddy/sharing-sources.php */
1919 if ( apply_filters( 'jetpack_register_post_for_share_counts', true, $post->ID, 'skype' ) ) {
1920 sharing_register_post_for_share_counts( $post->ID );
1921 }
1922 return $this->get_link(
1923 $this->get_process_request_url( $post->ID ), _x( 'Skype', 'share to', 'jetpack' ), __( 'Click to share on Skype', 'jetpack' ), 'share=skype', 'sharing-skype-' . $post->ID );
1924 }
1925
1926 /**
1927 * AMP display for Skype.
1928 *
1929 * @param \WP_Post $post The current post being viewed.
1930 */
1931 public function get_amp_display( $post ) {
1932 $attrs = array(
1933 'data-share-endpoint' => sprintf(
1934 'https://web.skype.com/share?url=%1$s&lang=%2$s=&source=jetpack',
1935 rawurlencode( $this->get_share_url( $post->ID ) ),
1936 'en-US'
1937 ),
1938 );
1939
1940 return $this->build_amp_markup( $attrs );
1941 }
1942
1943 public function process_request( $post, array $post_data ) {
1944 $skype_url = sprintf(
1945 'https://web.skype.com/share?url=%1$s&lang=%2$s=&source=jetpack',
1946 rawurlencode( $this->get_share_url( $post->ID ) ),
1947 'en-US'
1948 );
1949
1950 // Record stats
1951 parent::process_request( $post, $post_data );
1952
1953 // Redirect to Skype
1954 wp_redirect( $skype_url );
1955 die();
1956 }
1957
1958 public function display_footer() {
1959 if ( $this->smart ) :
1960 ?>
1961 <script>
1962 (function(r, d, s) {
1963 r.loadSkypeWebSdkAsync = r.loadSkypeWebSdkAsync || function(p) {
1964 var js, sjs = d.getElementsByTagName(s)[0];
1965 if (d.getElementById(p.id)) { return; }
1966 js = d.createElement(s);
1967 js.id = p.id;
1968 js.src = p.scriptToLoad;
1969 js.onload = p.callback
1970 sjs.parentNode.insertBefore(js, sjs);
1971 };
1972 var p = {
1973 scriptToLoad: 'https://swx.cdn.skype.com/shared/v/latest/skypewebsdk.js',
1974 id: 'skype_web_sdk'
1975 };
1976 r.loadSkypeWebSdkAsync(p);
1977 })(window, document, 'script');
1978 </script>
1979 <?php
1980 else :
1981 $this->js_dialog( $this->shortname, array( 'width' => 305, 'height' => 665 ) );
1982 endif;
1983 }
1984 }
1985