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