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