PluginProbe ʕ •ᴥ•ʔ
Jetpack – WP Security, Backup, Speed, & Growth / 14.6.1
Jetpack – WP Security, Backup, Speed, & Growth v14.6.1
12.0.3 12.1.3 12.2.3 12.3.2 12.4.2 12.5.2 12.6.4 12.7.3 12.8.3 12.9.5 13.0.2 13.1.5 13.2.4 13.3.3 13.4.5 13.5.2 13.6.2 13.7.2 13.8.3 13.9.2 14.0.1 14.1.1 14.2.2 14.3.1 14.4.2 14.5.1 14.6.1 14.7.1 14.8.1 14.9.2 15.0.3 15.1.2 15.2.1 15.3.2 15.4.1 15.5.1 15.6.1 15.7.2 15.8.1 15.9.2 16.0.2 16.1.3 16.2-a.5 16.2-a.3 16.1.2 16.2-a.1 16.1.1 16.1 16.1-beta 16.1-beta.2 16.1-beta.3 16.1-a.5 16.1-a.3 16.0.1 16.1-a.1 16.0 16.0-beta 16.0-a.7 16.0-a.5 15.9.1 16.0-a.3 16.0-a.1 15.9 15.9-beta 15.9-a.7 15.9-a.5 15.9-a.3 15.9-a.1 15.8 15.8-beta 15.8-a.7 15.8-a.5 5.2.5 5.3.4 5.4.4 5.5.5 5.6.5 5.7.5 5.8.4 5.9.4 6.0.4 6.1 6.1.1 6.1.2 6.1.3 6.1.4 6.1.5 6.2 6.2.1 6.2.2 6.2.3 6.2.4 6.2.5 6.3 6.3.1 6.3.2 6.3.3 6.3.4 6.3.5 6.3.6 6.3.7 6.4 6.4.1 6.4.2 6.4.3 6.4.4 6.4.5 6.4.6 6.5 6.5.1 6.5.2 6.5.3 6.5.4 6.6 6.6.1 6.6.2 6.6.3 6.6.4 6.6.5 6.7 6.7.1 6.7.2 6.7.3 6.7.4 6.8 6.8.1 6.8.2 6.8.3 6.8.4 6.8.5 6.9 6.9.1 6.9.2 6.9.3 6.9.4 7.0 7.0.1 7.0.2 7.0.3 7.0.4 7.0.5 7.1 7.1.1 7.1.2 7.1.3 7.1.4 7.1.5 7.2 7.2.1 7.2.1.1 7.2.2 7.2.3 7.2.4 7.2.5 7.3 7.3.0.1 7.3.1 7.3.1.1 7.3.2 7.3.3 7.3.4 7.3.5 7.4 7.4.1 7.4.2 7.4.3 7.4.4 7.4.5 7.5 7.5.0.1 7.5.1 7.5.2 7.5.3 7.5.4 7.5.5 7.5.6 7.5.7 7.6 7.6.1 7.6.2 7.6.3 7.6.4 7.7 7.7.1 7.7.2 7.7.3 7.7.4 7.7.5 7.7.6 7.8 7.8.1 7.8.2 7.8.3 7.8.4 7.9 7.9.1 7.9.2 7.9.3 7.9.4 8.0 8.0.1 8.0.2 8.0.3 8.1 8.1.1 8.1.2 8.1.3 8.1.4 8.2 8.2.0.1 8.2.1 8.2.2 8.2.3 8.2.4 8.2.5 8.2.6 8.3 8.3.1 8.3.2 8.3.3 8.4 8.4.1 8.4.2 8.4.3 8.4.4 8.4.5 8.5 8.5.1 8.5.2 8.5.3 8.6 8.6.1 8.6.2 8.6.3 8.6.4 8.7 8.7.0.1 8.7.1 8.7.2 8.7.3 8.7.4 8.8 8.8.1 8.8.2 8.8.3 8.8.4 8.8.5 8.9 8.9.1 8.9.2 8.9.3 8.9.4 9.0 9.0.1 9.0.2 9.0.3 9.0.4 9.0.5 9.1 9.1.1 9.1.2 9.1.3 9.2 9.2.1 9.2.2 9.2.3 9.2.4 9.3 9.3.1 9.3.2 9.3.3 9.3.4 9.3.5 9.4 9.4.1 9.4.2 9.4.3 9.4.4 9.5 9.5.1 9.5.2 9.5.3 9.5.4 9.5.5 9.6 9.6.1 9.6.2 9.6.3 9.6.4 9.7 9.7.1 9.7.2 15.7-beta.2 9.7.3 15.7.1 9.8 15.8-a.1 9.8.1 15.8-a.3 9.8.2 2.0.9 9.8.3 2.1.7 9.9 2.2.10 9.9.1 2.3.10 9.9.2 2.4.7 9.9.3 2.5.5 2.6.6 2.7.5 2.8.5 2.9.6 3.0.6 3.1.5 3.2.5 3.3.6 3.4.6 3.5.6 3.6.4 3.7.5 3.8.5 3.9.10 4.0.7 4.1.4 4.2.5 4.3.5 4.4.5 4.5.3 4.6.3 4.7.4 4.8.5 4.9.3 5.0.3 5.1.4 trunk 10.0 10.0.1 10.0.2 10.1 10.1.1 10.1.2 10.2 10.2.1 10.2.2 10.2.3 10.3 10.3.1 10.3.2 10.4 10.4.1 10.4.2 10.5 10.5.1 10.5.2 10.5.3 10.6 10.6.1 10.6.2 10.7 10.7.1 10.7.2 10.8 10.8.1 10.8.2 10.9 10.9.1 10.9.2 10.9.3 11.0 11.0.1 11.0.2 11.1 11.1.1 11.1.2 11.1.3 11.1.4 11.2 11.2.1 11.2.2 11.3 11.3.1 11.3.2 11.3.3 11.3.4 11.4 11.4.1 11.4.2 11.5 11.5.1 11.5.2 11.5.3 11.6 11.6.1 11.6.2 11.7 11.7.1 11.7.2 11.7.3 11.8 11.8.3 11.8.4 11.8.5 11.8.6 11.9 11.9.1 11.9.2 11.9.3 12.0 12.0.1 12.0.2 12.1 12.1.1 12.1.2 12.2 12.2.1 12.2.2 12.3 12.3.1 12.4 12.4.1 12.5 12.5.1 12.6 12.6.1 12.6.2 12.6.3 12.7 12.7.1 12.7.2 12.8 12.8.1 12.8.2 12.9 12.9.1 12.9.2 12.9.3 12.9.4 13.0 13.0.1 13.1 13.1.1 13.1.2 13.1.3 13.1.4 13.2 13.2.1 13.2.2 13.2.3 13.3 13.3.1 13.3.2 13.4 13.4.1 13.4.2 13.4.3 13.4.4 13.5 13.5.1 13.6 13.6.1 13.7 13.7.1 13.8 13.8.1 13.8.2 13.9 13.9.1 14.0 14.1 14.2 14.2.1 14.3 14.4 14.4.1 14.5 14.6 14.7 14.8 14.9 14.9.1 15.0 15.0.1 15.0.2 15.1 15.1.1 15.2 15.3 15.3.1 15.4 15.5 15.6 15.7 15.7-a.1 15.7-a.3 15.7-a.5 15.7-a.7 15.7-beta
jetpack / modules / sharedaddy / sharing-service.php
jetpack / modules / sharedaddy Last commit date
images 2 years ago services 1 year ago admin-sharing-rtl.css 1 year ago admin-sharing-rtl.min.css 1 year ago admin-sharing.css 1 year ago admin-sharing.js 1 year ago admin-sharing.min.css 1 year ago amp-sharing.css 1 year ago recaptcha.php 4 years ago sharedaddy.php 2 years ago sharing-service.php 1 year ago sharing-sources.php 1 year ago sharing.css 1 year ago sharing.js 3 years ago sharing.php 1 year ago
sharing-service.php
1281 lines
1 <?php // phpcs:ignore WordPress.Files.FileName.InvalidClassFileName
2 /**
3 * Utilities to register and interact with a sharing service.
4 *
5 * Sharing_Service gets info about a service.
6 * Sharing_Service_Total and Sharing_Post_Total get stats data.
7 *
8 * @package automattic/jetpack
9 *
10 * phpcs:disable Generic.Files.OneObjectStructurePerFile.MultipleFound
11 */
12
13 // phpcs:disable Universal.Files.SeparateFunctionsFromOO.Mixed -- TODO: Move classes to appropriately-named class files.
14
15 use Automattic\Jetpack\Assets;
16 use Automattic\Jetpack\Redirect;
17 use Automattic\Jetpack\Status;
18 use Automattic\Jetpack\Sync\Settings;
19
20 require_once __DIR__ . '/sharing-sources.php';
21
22 define( 'WP_SHARING_PLUGIN_VERSION', JETPACK__VERSION );
23
24 /**
25 * Interact with a sharing service.
26 */
27 class Sharing_Service {
28 /**
29 * Should the service be available globally?
30 *
31 * @var bool
32 */
33 private $global = false;
34
35 /**
36 * Default sharing label.
37 *
38 * @var string
39 */
40 public $default_sharing_label = '';
41
42 /**
43 * Initialize the sharing service.
44 * Only run this method once upon module loading.
45 *
46 * @return void
47 */
48 public static function init() {
49 add_filter( 'the_content', 'sharing_display', 19 );
50 add_filter( 'the_excerpt', 'sharing_display', 19 );
51 }
52
53 /**
54 * Constructor.
55 */
56 public function __construct() {
57 $this->default_sharing_label = __( 'Share this:', 'jetpack' );
58 }
59
60 /**
61 * Gets a generic list of all services, without any config
62 *
63 * @return array
64 */
65 public function get_all_services_blog() {
66 $options = get_option( 'sharing-options' );
67 $all = $this->get_all_services();
68 $services = array();
69
70 foreach ( $all as $id => $name ) {
71 if ( isset( $all[ $id ] ) ) {
72 $config = array();
73
74 // Pre-load custom modules otherwise they won't know who they are
75 if ( str_starts_with( $id, 'custom-' ) && is_array( $options[ $id ] ) ) {
76 $config = $options[ $id ];
77 }
78
79 $services[ $id ] = new $all[ $id ]( $id, $config );
80 }
81 }
82
83 return $services;
84 }
85
86 /**
87 * Gets a list of all available service names and classes
88 *
89 * @param bool $include_custom Include custom sharing services.
90 *
91 * @return array
92 */
93 public function get_all_services( $include_custom = true ) {
94 // Default services
95 // if you update this list, please update the REST API tests
96 // in bin/tests/api/suites/SharingTest.php
97 $services = array(
98 'print' => 'Share_Print',
99 'email' => 'Share_Email',
100 'facebook' => 'Share_Facebook',
101 'linkedin' => 'Share_LinkedIn',
102 'reddit' => 'Share_Reddit',
103 'twitter' => 'Share_Twitter',
104 'tumblr' => 'Share_Tumblr',
105 'pinterest' => 'Share_Pinterest',
106 'pocket' => 'Share_Pocket',
107 'telegram' => 'Share_Telegram',
108 'threads' => 'Share_Threads',
109 'jetpack-whatsapp' => 'Jetpack_Share_WhatsApp',
110 'mastodon' => 'Share_Mastodon',
111 'nextdoor' => 'Share_Nextdoor',
112 'x' => 'Share_X',
113 'bluesky' => 'Share_Bluesky',
114 );
115
116 if ( is_multisite() && is_plugin_active( 'press-this/press-this-plugin.php' ) ) {
117 $services['press-this'] = 'Share_PressThis';
118 }
119
120 if ( $include_custom ) {
121 // Add any custom services in
122 $options = $this->get_global_options();
123 foreach ( (array) $options['custom'] as $custom_id ) {
124 $services[ $custom_id ] = 'Share_Custom';
125 }
126 }
127
128 /**
129 * Filters the list of available Sharing Services.
130 *
131 * @module sharedaddy
132 *
133 * @since 1.1.0
134 *
135 * @param array $services Array of all available Sharing Services.
136 */
137 return apply_filters( 'sharing_services', $services );
138 }
139
140 /**
141 * Save a new custom sharing service.
142 *
143 * @param string $label Service name.
144 * @param string $url Service sharing URL.
145 * @param string $icon Service icon.
146 *
147 * @return bool|Share_Custom
148 */
149 public function new_service( $label, $url, $icon ) {
150 // Validate.
151 $label = trim( wp_html_excerpt( wp_kses( $label, array() ), 30 ) );
152 $url = trim( esc_url_raw( $url ) );
153 $icon = trim( esc_url_raw( $icon ) );
154
155 if ( $label && $url && $icon ) {
156 $options = get_option( 'sharing-options' );
157 if ( ! is_array( $options ) ) {
158 $options = array();
159 }
160
161 $service_id = 'custom-' . time();
162
163 // Add a new custom service
164 $options['global']['custom'][] = $service_id;
165 if ( false !== $this->global ) {
166 $this->global['custom'][] = $service_id;
167 }
168
169 update_option( 'sharing-options', $options );
170
171 // Create a custom service and set the options for it
172 $service = new Share_Custom(
173 $service_id,
174 array(
175 'name' => $label,
176 'url' => $url,
177 'icon' => $icon,
178 )
179 );
180 $this->set_service( $service_id, $service );
181
182 // Return the service
183 return $service;
184 }
185
186 return false;
187 }
188
189 /**
190 * Delete a sharing service.
191 *
192 * @param string $service_id Service ID.
193 *
194 * @return bool
195 */
196 public function delete_service( $service_id ) {
197 $options = get_option( 'sharing-options' );
198 if ( isset( $options[ $service_id ] ) ) {
199 unset( $options[ $service_id ] );
200 }
201
202 $key = array_search( $service_id, $options['global']['custom'], true );
203 if ( $key !== false ) {
204 unset( $options['global']['custom'][ $key ] );
205 }
206
207 update_option( 'sharing-options', $options );
208 return true;
209 }
210
211 /**
212 * Save enabled sharing services.
213 *
214 * @param array $visible Visible sharing services.
215 * @param array $hidden Hidden sharing services (available under a dropdown).
216 *
217 * @return bool
218 */
219 public function set_blog_services( array $visible, array $hidden ) {
220 $services = $this->get_all_services();
221 // Validate the services
222 $available = array_keys( $services );
223
224 // Only allow services that we have defined
225 $hidden = array_intersect( $hidden, $available );
226 $visible = array_intersect( $visible, $available );
227
228 // Ensure we don't have the same ones in hidden and visible
229 $hidden = array_diff( $hidden, $visible );
230
231 /**
232 * Control the state of the list of sharing services.
233 *
234 * @module sharedaddy
235 *
236 * @since 1.1.0
237 *
238 * @param array $args {
239 * Array of options describing the state of the sharing services.
240 *
241 * @type array $services List of all available service names and classes.
242 * @type array $available Validated list of all available service names and classes.
243 * @type array $hidden List of services hidden behind a "More" button.
244 * @type array $visible List of visible services.
245 * @type array $this->get_blog_services() Array of Sharing Services currently enabled.
246 * }
247 */
248 do_action(
249 'sharing_get_services_state',
250 array(
251 'services' => $services,
252 'available' => $available,
253 'hidden' => $hidden,
254 'visible' => $visible,
255 'currently_enabled' => $this->get_blog_services(),
256 )
257 );
258
259 return update_option(
260 'sharing-services',
261 array(
262 'visible' => $visible,
263 'hidden' => $hidden,
264 )
265 );
266 }
267
268 /**
269 * Get information about enabled sharing services on the site.
270 *
271 * @return array
272 */
273 public function get_blog_services() {
274 $options = get_option( 'sharing-options' );
275 $enabled = get_option( 'sharing-services' );
276 $services = $this->get_all_services();
277
278 /**
279 * Check if options exist and are well formatted.
280 * This avoids issues on sites with corrupted options.
281 *
282 * @see https://github.com/Automattic/jetpack/issues/6121
283 */
284 if ( ! is_array( $options ) || ! isset( $options['button_style'] ) || ! isset( $options['global'] ) ) {
285 $global_options = array( 'global' => $this->get_global_options() );
286 $options = is_array( $options )
287 ? array_merge( $options, $global_options )
288 : $global_options;
289 }
290
291 $global = $options['global'];
292
293 // Default services
294 if ( ! is_array( $enabled ) ) {
295 $enabled = array(
296 'visible' => array(
297 'facebook',
298 'x',
299 ),
300 'hidden' => array(),
301 );
302
303 /**
304 * Filters the list of default Sharing Services.
305 *
306 * @module sharedaddy
307 *
308 * @since 1.1.0
309 *
310 * @param array $enabled Array of default Sharing Services.
311 */
312 $enabled = apply_filters( 'sharing_default_services', $enabled );
313 }
314
315 // Cleanup after any filters that may have produced duplicate services
316 if ( is_array( $enabled['visible'] ) ) {
317 $enabled['visible'] = array_unique( $enabled['visible'] );
318 } else {
319 $enabled['visible'] = array();
320 }
321
322 if ( is_array( $enabled['hidden'] ) ) {
323 $enabled['hidden'] = array_unique( $enabled['hidden'] );
324 } else {
325 $enabled['hidden'] = array();
326 }
327
328 // Form the enabled services
329 $blog = array(
330 'visible' => array(),
331 'hidden' => array(),
332 );
333
334 foreach ( $blog as $area => $stuff ) {
335 foreach ( (array) $enabled[ $area ] as $service ) {
336 if ( isset( $services[ $service ] ) ) {
337 if ( ! isset( $options[ $service ] ) || ! is_array( $options[ $service ] ) ) {
338 $options[ $service ] = array();
339 }
340 $blog[ $area ][ $service ] = new $services[ $service ]( $service, array_merge( $global, $options[ $service ] ) );
341 }
342 }
343 }
344
345 /**
346 * Filters the list of enabled Sharing Services.
347 *
348 * @module sharedaddy
349 *
350 * @since 1.1.0
351 *
352 * @param array $blog Array of enabled Sharing Services.
353 */
354 $blog = apply_filters( 'sharing_services_enabled', $blog );
355
356 // Add CSS for NASCAR
357 if ( ( is_countable( $blog['visible'] ) && count( $blog['visible'] ) ) || ( is_countable( $blog['hidden'] ) && count( $blog['hidden'] ) ) ) {
358 add_filter( 'post_flair_block_css', 'post_flair_service_enabled_sharing' );
359 }
360
361 // Convenience for checking if a service is present
362 $blog['all'] = array_flip( array_merge( array_keys( $blog['visible'] ), array_keys( $blog['hidden'] ) ) );
363 return $blog;
364 }
365
366 /**
367 * Get information about a specific enabled sharing service.
368 *
369 * @param string $service_name Service name.
370 *
371 * @return bool|Sharing_Source
372 */
373 public function get_service( $service_name ) {
374 $services = $this->get_blog_services();
375
376 if ( isset( $services['visible'][ $service_name ] ) ) {
377 return $services['visible'][ $service_name ];
378 }
379
380 if ( isset( $services['hidden'][ $service_name ] ) ) {
381 return $services['hidden'][ $service_name ];
382 }
383
384 return false;
385 }
386
387 /**
388 * Update global sharing options.
389 *
390 * @param array $data Array of new sharing options to save.
391 */
392 public function set_global_options( $data ) {
393 $options = get_option( 'sharing-options' );
394
395 // No options yet.
396 if ( ! is_array( $options ) ) {
397 $options = array();
398 }
399
400 // Defaults.
401 $options['global'] = array(
402 'button_style' => 'icon-text',
403 'sharing_label' => $this->default_sharing_label,
404 'open_links' => 'same',
405 'show' => ! isset( $options['global'] ) ? array( 'post', 'page' ) : array(),
406 'custom' => isset( $options['global']['custom'] ) ? $options['global']['custom'] : array(),
407 );
408
409 /**
410 * Filters global sharing settings.
411 *
412 * @module sharedaddy
413 *
414 * @since 1.1.0
415 *
416 * @param array $options['global'] Array of global sharing settings.
417 */
418 $options['global'] = apply_filters( 'sharing_default_global', $options['global'] );
419
420 // Validate options and set from our data
421 if (
422 isset( $data['button_style'] )
423 && in_array( $data['button_style'], array( 'icon-text', 'icon', 'text', 'official' ), true )
424 ) {
425 $options['global']['button_style'] = $data['button_style'];
426 }
427
428 if ( isset( $data['sharing_label'] ) ) {
429 if ( $this->default_sharing_label === $data['sharing_label'] ) {
430 $options['global']['sharing_label'] = false;
431 } else {
432 $options['global']['sharing_label'] = trim( wp_kses( stripslashes( $data['sharing_label'] ), array() ) );
433 }
434 }
435
436 if (
437 isset( $data['open_links'] )
438 && in_array( $data['open_links'], array( 'new', 'same' ), true )
439 ) {
440 $options['global']['open_links'] = $data['open_links'];
441 }
442
443 $shows = array_values( get_post_types( array( 'public' => true ) ) );
444 $shows[] = 'index';
445 if ( isset( $data['show'] ) ) {
446 if ( is_scalar( $data['show'] ) ) {
447 switch ( $data['show'] ) {
448 case 'posts':
449 $data['show'] = array( 'post', 'page' );
450 break;
451 case 'index':
452 $data['show'] = array( 'index' );
453 break;
454 case 'posts-index':
455 $data['show'] = array( 'post', 'page', 'index' );
456 break;
457 }
458 }
459
460 $data['show'] = array_intersect( $data['show'], $shows );
461 if ( $data['show'] ) {
462 $options['global']['show'] = $data['show'];
463 }
464 }
465
466 update_option( 'sharing-options', $options );
467 return $options['global'];
468 }
469
470 /**
471 * Get global sharing options for the site.
472 *
473 * @return array
474 */
475 public function get_global_options() {
476 if ( $this->global === false ) {
477 $options = get_option( 'sharing-options' );
478
479 if ( is_array( $options ) && isset( $options['global'] ) && is_array( $options['global'] ) ) {
480 $this->global = $options['global'];
481 } else {
482 $this->global = $this->set_global_options( $options );
483 }
484 }
485
486 if ( ! isset( $this->global['show'] ) ) {
487 $this->global['show'] = array( 'post', 'page' );
488 } elseif ( is_scalar( $this->global['show'] ) ) {
489 switch ( $this->global['show'] ) {
490 case 'posts':
491 $this->global['show'] = array( 'post', 'page' );
492 break;
493 case 'index':
494 $this->global['show'] = array( 'index' );
495 break;
496 case 'posts-index':
497 $this->global['show'] = array( 'post', 'page', 'index' );
498 break;
499 }
500 }
501
502 if ( false === $this->global['sharing_label'] || $this->global['sharing_label'] === 'Share this:' ) {
503 $this->global['sharing_label'] = $this->default_sharing_label;
504 }
505
506 return $this->global;
507 }
508
509 /**
510 * Save a sharing service for use.
511 *
512 * @param int $id Sharing unique ID.
513 * @param Sharing_Advanced_Source $service Sharing service.
514 *
515 * @return void
516 */
517 public function set_service( $id, Sharing_Advanced_Source $service ) {
518 // Update the options for this service
519 $options = get_option( 'sharing-options' );
520
521 // No options yet
522 if ( ! is_array( $options ) ) {
523 $options = array();
524 }
525
526 /**
527 * Get the state of a sharing button.
528 *
529 * @module sharedaddy
530 *
531 * @since 1.1.0
532 *
533 * @param array $args {
534 * State of a sharing button.
535 *
536 * @type string $id Service ID.
537 * @type array $options Array of all sharing options.
538 * @type array $service Details about a service.
539 * }
540 */
541 do_action(
542 'sharing_get_button_state',
543 array(
544 'id' => $id,
545 'options' => $options,
546 'service' => $service,
547 )
548 );
549
550 $options[ $id ] = $service->get_options();
551
552 update_option( 'sharing-options', array_filter( $options ) );
553 }
554
555 /**
556 * Get stats for a site, a post, or a sharing service.
557 * Soon to come to a .org plugin near you!
558 *
559 * @param string|bool $service_name Service name.
560 * @param int|bool $post_id Post ID.
561 * @param int|bool $_blog_id Blog ID.
562 *
563 * @return int
564 */
565 public function get_total( $service_name = false, $post_id = false, $_blog_id = false ) {
566 global $wpdb, $blog_id;
567 if ( ! $_blog_id ) {
568 $_blog_id = $blog_id;
569 }
570 if ( $service_name === false ) {
571 if ( $post_id > 0 ) {
572 // total number of shares for this post
573 $sql = $wpdb->prepare( 'SELECT SUM( count ) FROM sharing_stats WHERE blog_id = %d AND post_id = %d', $_blog_id, $post_id );
574 $cache_key = "sharing_service_get_total_b{$_blog_id}_p{$post_id}";
575 } else {
576 // total number of shares for this blog
577 $sql = $wpdb->prepare( 'SELECT SUM( count ) FROM sharing_stats WHERE blog_id = %d', $_blog_id );
578 $cache_key = "sharing_service_get_total_b{$_blog_id}";
579 }
580 } elseif ( $post_id > 0 ) {
581 $sql = $wpdb->prepare( 'SELECT SUM( count ) FROM sharing_stats WHERE blog_id = %d AND post_id = %d AND share_service = %s', $_blog_id, $post_id, $service_name );
582 $cache_key = "sharing_service_get_total_b{$_blog_id}_p{$post_id}_s{$service_name}";
583 } else {
584 $sql = $wpdb->prepare( 'SELECT SUM( count ) FROM sharing_stats WHERE blog_id = %d AND share_service = %s', $_blog_id, $service_name );
585 $cache_key = "sharing_service_get_total_b{$_blog_id}_s{$service_name}";
586 }
587
588 $ret = wp_cache_get( $cache_key, 'sharing' );
589 if ( $ret === false ) {
590 // phpcs:ignore WordPress.DB.PreparedSQL.NotPrepared, WordPress.DB.DirectDatabaseQuery.DirectQuery -- Prepared above.
591 $ret = (int) $wpdb->get_var( $sql );
592 wp_cache_set( $cache_key, $ret, 'sharing', 5 * MINUTE_IN_SECONDS );
593 }
594 return $ret;
595 }
596
597 /**
598 * Get total stats for a site, for all sharing services.
599 *
600 * @param int|bool $post_id Post ID.
601 *
602 * @return array
603 */
604 public function get_services_total( $post_id = false ) {
605 $totals = array();
606 $services = $this->get_blog_services();
607
608 if ( ! empty( $services ) && isset( $services['all'] ) ) {
609 foreach ( $services['all'] as $key => $value ) {
610 $totals[ $key ] = new Sharing_Service_Total( $key, $this->get_total( $key, $post_id ) );
611 }
612 }
613 usort( $totals, array( 'Sharing_Service_Total', 'cmp' ) );
614
615 return $totals;
616 }
617
618 /**
619 * Get sharing stats for all posts on the site.
620 *
621 * @return array
622 */
623 public function get_posts_total() {
624 $totals = array();
625 global $wpdb, $blog_id;
626
627 $cache_key = "sharing_service_get_posts_total_{$blog_id}";
628 $my_data = wp_cache_get( $cache_key, 'sharing' );
629 if ( $my_data === false ) {
630 $my_data = $wpdb->get_results( $wpdb->prepare( 'SELECT post_id as id, SUM( count ) as total FROM sharing_stats WHERE blog_id = %d GROUP BY post_id ORDER BY count DESC ', $blog_id ) ); // phpcs:ignore WordPress.DB.DirectDatabaseQuery.DirectQuery
631 wp_cache_set( $cache_key, $my_data, 'sharing', 5 * MINUTE_IN_SECONDS );
632 }
633
634 if ( ! empty( $my_data ) ) {
635 foreach ( $my_data as $row ) {
636 $totals[] = new Sharing_Post_Total( $row->id, $row->total );
637 }
638 }
639
640 usort( $totals, array( 'Sharing_Post_Total', 'cmp' ) );
641
642 return $totals;
643 }
644 }
645
646 /**
647 * Get stats for a specific sharing service.
648 */
649 class Sharing_Service_Total {
650 /**
651 * Sharing service ID.
652 *
653 * @var int
654 */
655 public $id = '';
656
657 /**
658 * Service name.
659 *
660 * @var string
661 */
662 public $name = '';
663
664 /**
665 * Sharing service name.
666 *
667 * @var string
668 */
669 public $service = '';
670
671 /**
672 * Total number of shares for this service.
673 *
674 * @var string
675 */
676 public $total = 0;
677
678 /**
679 * Constructor.
680 *
681 * @param int $id Service ID.
682 * @param int $total Total shares.
683 */
684 public function __construct( $id, $total ) {
685 $services = new Sharing_Service();
686 $this->id = esc_html( $id );
687 $this->service = $services->get_service( $id );
688 $this->total = (int) $total;
689
690 $this->name = $this->service->get_name();
691 }
692
693 /**
694 * Compare total shares between 2 posts.
695 *
696 * @param object $a Sharing_Service_Total object.
697 * @param object $b Sharing_Service_Total object.
698 *
699 * @return int -1, 0, or 1 if $a is <, =, or > $b
700 */
701 public static function cmp( $a, $b ) {
702 if ( $a->total === $b->total ) {
703 return $b->name <=> $a->name;
704 }
705 return $b->total <=> $a->total;
706 }
707 }
708
709 /**
710 * Get sharing stats for a specific post.
711 */
712 class Sharing_Post_Total {
713 /**
714 * Sharing service ID.
715 *
716 * @var int
717 */
718 public $id = 0;
719
720 /**
721 * Total shares.
722 *
723 * @var int
724 */
725 public $total = 0;
726
727 /**
728 * Post title.
729 *
730 * @var string
731 */
732 public $title = '';
733
734 /**
735 * Post permalink.
736 *
737 * @var string
738 */
739 public $url = '';
740
741 /**
742 * Constructor.
743 *
744 * @param int $id Service ID.
745 * @param int $total Total shares.
746 */
747 public function __construct( $id, $total ) {
748 $this->id = (int) $id;
749 $this->total = (int) $total;
750 $this->title = get_the_title( $this->id );
751 $this->url = get_permalink( $this->id );
752 }
753
754 /**
755 * Compare total shares between 2 posts.
756 *
757 * @param object $a Sharing_Post_Total object.
758 * @param object $b Sharing_Post_Total object.
759 *
760 * @return int -1, 0, or 1 if $a is <, =, or > $b
761 */
762 public static function cmp( $a, $b ) {
763 if ( $a->total === $b->total ) {
764 return $b->id <=> $a->id;
765 }
766 return $b->total <=> $a->total;
767 }
768 }
769
770 /**
771 * Populate sharing counts global with a post we want to count shares for.
772 *
773 * @param int $post_id Post ID.
774 *
775 * @return void
776 */
777 function sharing_register_post_for_share_counts( $post_id ) {
778 global $jetpack_sharing_counts;
779
780 if ( ! isset( $jetpack_sharing_counts ) || ! is_array( $jetpack_sharing_counts ) ) {
781 $jetpack_sharing_counts = array();
782 }
783
784 $jetpack_sharing_counts[ (int) $post_id ] = get_permalink( $post_id );
785 }
786
787 /**
788 * Determine whether we should load sharing scripts or not.
789 *
790 * @return bool
791 */
792 function sharing_maybe_enqueue_scripts() {
793 $sharer = new Sharing_Service();
794 $global_options = $sharer->get_global_options();
795
796 $enqueue = false;
797 if ( is_singular() && in_array( get_post_type(), $global_options['show'], true ) ) {
798 $enqueue = true;
799 } elseif (
800 in_array( 'index', $global_options['show'], true )
801 && (
802 is_home()
803 || is_front_page()
804 || is_archive()
805 || is_search()
806 || in_array( get_post_type(), $global_options['show'], true )
807 )
808 ) {
809 $enqueue = true;
810 }
811
812 /**
813 * Filter to decide when sharing scripts should be enqueued.
814 *
815 * @module sharedaddy
816 *
817 * @since 3.2.0
818 *
819 * @param bool $enqueue Decide if the sharing scripts should be enqueued.
820 */
821 return (bool) apply_filters( 'sharing_enqueue_scripts', $enqueue );
822 }
823
824 /**
825 * Add sharing JavaScript to the footer of a page.
826 *
827 * @return void
828 */
829 function sharing_add_footer() {
830 if (
831 class_exists( 'Jetpack_AMP_Support' )
832 && Jetpack_AMP_Support::is_amp_request()
833 ) {
834 return;
835 }
836
837 global $jetpack_sharing_counts;
838
839 if (
840 /**
841 * Filter all JavaScript output by the sharing module.
842 *
843 * @module sharedaddy
844 *
845 * @since 1.1.0
846 *
847 * @param bool true Control whether the sharing module should add any JavaScript to the site. Default to true.
848 */
849 apply_filters( 'sharing_js', true )
850 && sharing_maybe_enqueue_scripts()
851 ) {
852 if (
853 /**
854 * Filter the display of sharing counts next to the sharing buttons.
855 *
856 * @module sharedaddy
857 *
858 * @since 3.2.0
859 *
860 * @param bool true Control the display of counters next to the sharing buttons. Default to true.
861 */
862 apply_filters( 'jetpack_sharing_counts', true )
863 && is_array( $jetpack_sharing_counts )
864 && count( $jetpack_sharing_counts )
865 ) :
866 $sharing_post_urls = array_filter( $jetpack_sharing_counts );
867 if ( $sharing_post_urls ) :
868 ?>
869
870 <script type="text/javascript">
871 window.WPCOM_sharing_counts = <?php echo wp_json_encode( array_flip( $sharing_post_urls ) ); ?>;
872 </script>
873 <?php
874 endif;
875 endif;
876
877 wp_enqueue_script( 'sharing-js' );
878 $sharing_js_options = array(
879 'lang' => get_base_recaptcha_lang_code(),
880 /** This filter is documented in modules/sharedaddy/sharing-service.php */
881 'counts' => apply_filters( 'jetpack_sharing_counts', true ),
882 'is_stats_active' => Jetpack::is_module_active( 'stats' ),
883 );
884 wp_localize_script( 'sharing-js', 'sharing_js_options', $sharing_js_options );
885 }
886 $sharer = new Sharing_Service();
887 $enabled = $sharer->get_blog_services();
888 foreach ( array_merge( $enabled['visible'], $enabled['hidden'] ) as $service ) {
889 $service->display_footer();
890 }
891 }
892
893 /**
894 * Enqueue sharing CSS in head.
895 *
896 * @return void
897 */
898 function sharing_add_header() {
899 $sharer = new Sharing_Service();
900 $enabled = $sharer->get_blog_services();
901
902 foreach ( array_merge( $enabled['visible'], $enabled['hidden'] ) as $service ) {
903 $service->display_header();
904 }
905
906 if ( is_countable( $enabled['all'] ) && ( count( $enabled['all'] ) > 0 ) && sharing_maybe_enqueue_scripts() ) {
907 wp_enqueue_style( 'sharedaddy', plugin_dir_url( __FILE__ ) . 'sharing.css', array(), JETPACK__VERSION );
908 wp_enqueue_style( 'social-logos' );
909 }
910 }
911 add_action( 'wp_head', 'sharing_add_header', 1 );
912
913 /**
914 * Launch sharing requests on page load when a specific query string is used.
915 *
916 * @return void
917 */
918 function sharing_process_requests() {
919 global $post;
920
921 // Only process if: single post and share=X defined
922 if ( ( is_page() || is_single() ) && isset( $_GET['share'] ) && is_string( $_GET['share'] ) ) { // phpcs:ignore WordPress.Security.NonceVerification.Recommended
923 $sharer = new Sharing_Service();
924
925 $service = $sharer->get_service( sanitize_text_field( wp_unslash( $_GET['share'] ) ) ); // phpcs:ignore WordPress.Security.NonceVerification.Recommended
926 if ( $service ) {
927 $service->process_request( $post, $_POST ); // phpcs:ignore WordPress.Security.NonceVerification.Missing
928 }
929 }
930 }
931
932 // phpcs:ignore WordPress.Security.NonceVerification.Recommended -- Only checking for the data being present.
933 if ( isset( $_GET['share'] ) ) {
934 add_action( 'template_redirect', 'sharing_process_requests', 9 );
935 }
936
937 /**
938 * Gets the url to customise the sharing buttons in Calypso.
939 *
940 * @return string the customisation URL or null if it couldn't be determinde.
941 */
942 function get_sharing_buttons_customisation_url() {
943 return Redirect::get_url( 'calypso-marketing-sharing-buttons', array( 'site' => ( new Status() )->get_site_suffix() ) );
944 }
945
946 /**
947 * Append sharing links to text.
948 *
949 * @param string $text The original text to append sharing links onto.
950 * @param bool $echo Where to echo the text or return.
951 *
952 * @return string The original $text with, if conditions are met, the sharing links.
953 */
954 function sharing_display( $text = '', $echo = false ) {
955 global $post, $wp_current_filter;
956
957 if ( Settings::is_syncing() ) {
958 return $text;
959 }
960
961 // We require the post to not be empty and be an actual WordPress post object. If it's not - we just return.
962 if ( empty( $post ) || ! $post instanceof \WP_Post ) {
963 return $text;
964 }
965
966 if ( ( is_preview() || is_admin() ) && ! ( defined( 'DOING_AJAX' ) && DOING_AJAX ) ) {
967 return $text;
968 }
969
970 // Prevent from rendering sharing buttons in block which is fetched from REST endpoint by editor
971 if ( defined( 'REST_REQUEST' ) && REST_REQUEST ) {
972 return $text;
973 }
974
975 // Do not output sharing buttons for ActivityPub requests.
976 if (
977 function_exists( '\Activitypub\is_activitypub_request' )
978 && \Activitypub\is_activitypub_request()
979 ) {
980 return $text;
981 }
982
983 // Don't output flair on excerpts.
984 if ( in_array( 'get_the_excerpt', (array) $wp_current_filter, true ) ) {
985 return $text;
986 }
987
988 // Ensure we don't display sharing buttons on post excerpts that are hooked inside the post content
989 if ( in_array( 'the_excerpt', (array) $wp_current_filter, true ) &&
990 in_array( 'the_content', (array) $wp_current_filter, true ) ) {
991 return $text;
992 }
993
994 // Don't allow flair to be added to the_content more than once (prevent infinite loops).
995 $done = false;
996 foreach ( $wp_current_filter as $filter ) {
997 if ( 'the_content' === $filter ) {
998 if ( $done ) {
999 return $text;
1000 } else {
1001 $done = true;
1002 }
1003 }
1004 }
1005
1006 // check whether we are viewing the front page and whether the front page option is checked.
1007 $options = get_option( 'sharing-options' );
1008 $display_options = null;
1009
1010 if ( is_array( $options ) ) {
1011 $display_options = $options['global']['show'];
1012 }
1013
1014 if ( is_front_page() && ( is_array( $display_options ) && ! in_array( 'index', $display_options, true ) ) ) {
1015 return $text;
1016 }
1017
1018 if ( is_attachment() && in_array( 'the_excerpt', (array) $wp_current_filter, true ) ) {
1019 // Many themes run the_excerpt() conditionally on an attachment page, then run the_content().
1020 // We only want to output the sharing buttons once. Let's stick with the_content().
1021 return $text;
1022 }
1023
1024 $sharer = new Sharing_Service();
1025 $global = $sharer->get_global_options();
1026
1027 $show = false;
1028 if ( ! is_feed() ) {
1029 if ( is_singular() && in_array( get_post_type(), $global['show'], true ) ) {
1030 $show = true;
1031 } elseif ( in_array( 'index', $global['show'], true ) && ( is_home() || is_front_page() || is_archive() || is_search() || in_array( get_post_type(), $global['show'], true ) ) ) {
1032 $show = true;
1033 }
1034 }
1035
1036 /**
1037 * Filter to decide if sharing buttons should be displayed.
1038 *
1039 * @module sharedaddy
1040 *
1041 * @since 1.1.0
1042 *
1043 * @param bool $show Should the sharing buttons be displayed.
1044 * @param WP_Post $post The post to share.
1045 */
1046 $show = apply_filters( 'sharing_show', $show, $post );
1047
1048 // Disabled for this post?
1049 $switched_status = get_post_meta( $post->ID, 'sharing_disabled', false );
1050
1051 if ( ! empty( $switched_status ) ) {
1052 $show = false;
1053 }
1054
1055 // Is the post private?
1056 $post_status = get_post_status( $post->ID );
1057
1058 if ( 'private' === $post_status ) {
1059 $show = false;
1060 }
1061
1062 // Hide on password protected posts unless password is provided.
1063 if ( post_password_required( $post->ID ) ) {
1064 $show = false;
1065 }
1066
1067 /**
1068 * Filter the Sharing buttons' Ajax action name Jetpack checks for.
1069 * This allows the use of the buttons with your own Ajax implementation.
1070 *
1071 * @module sharedaddy
1072 *
1073 * @since 7.3.0
1074 *
1075 * @param string $sharing_ajax_action_name Name of the Sharing buttons' Ajax action.
1076 */
1077 $ajax_action = apply_filters( 'sharing_ajax_action', 'get_latest_posts' );
1078
1079 // Allow to be used in ajax requests for latest posts.
1080 if (
1081 defined( 'DOING_AJAX' )
1082 && DOING_AJAX
1083 && isset( $_REQUEST['action'] ) // phpcs:ignore WordPress.Security.NonceVerification.Recommended -- Nonce handling happens within each custom implementation.
1084 && $ajax_action === $_REQUEST['action'] // phpcs:ignore WordPress.Security.NonceVerification.Recommended -- Nonce handling happens within each custom implementation.
1085 ) {
1086 $show = true;
1087 }
1088
1089 $sharing_content = '';
1090 $enabled = false;
1091
1092 if ( $show ) {
1093 /**
1094 * Filters the list of enabled Sharing Services.
1095 *
1096 * @module sharedaddy
1097 *
1098 * @since 2.2.3
1099 *
1100 * @param array $sharer->get_blog_services() Array of Sharing Services currently enabled.
1101 */
1102 $enabled = apply_filters( 'sharing_enabled', $sharer->get_blog_services() );
1103
1104 if ( is_countable( $enabled['all'] ) && ( count( $enabled['all'] ) > 0 ) ) {
1105 $dir = get_option( 'text_direction' );
1106
1107 // Wrapper.
1108 $sharing_content .= '<div class="sharedaddy sd-sharing-enabled"><div class="robots-nocontent sd-block sd-social sd-social-' . $global['button_style'] . ' sd-sharing">';
1109 if ( '' !== $global['sharing_label'] ) {
1110 $sharing_content .= sprintf(
1111 /**
1112 * Filter the sharing buttons' headline structure.
1113 *
1114 * @module sharedaddy
1115 *
1116 * @since 4.4.0
1117 *
1118 * @param string $sharing_headline Sharing headline structure.
1119 * @param string $global['sharing_label'] Sharing title.
1120 * @param string $sharing Module name.
1121 */
1122 apply_filters( 'jetpack_sharing_headline_html', '<h3 class="sd-title">%s</h3>', $global['sharing_label'], 'sharing' ),
1123 esc_html( $global['sharing_label'] )
1124 );
1125 }
1126 $sharing_content .= '<div class="sd-content"><ul>';
1127
1128 // Visible items.
1129 $visible = '';
1130 foreach ( $enabled['visible'] as $service ) {
1131 $klasses = array( 'share-' . $service->get_class() );
1132 if ( $service->is_deprecated() ) {
1133 if ( ! current_user_can( 'manage_options' ) ) {
1134 continue;
1135 }
1136 $klasses[] = 'share-deprecated';
1137 }
1138 // Individual HTML for sharing service.
1139 $visible .= '<li class="' . implode( ' ', $klasses ) . '">' . $service->get_display( $post ) . '</li>';
1140 }
1141
1142 $parts = array();
1143 $parts[] = $visible;
1144 $count_hidden = is_countable( $enabled['hidden'] ) ? count( $enabled['hidden'] ) : 0;
1145 $count_visible = is_countable( $enabled['visible'] ) ? count( $enabled['visible'] ) : 0;
1146 if ( $count_hidden > 0 ) {
1147 if ( $count_visible > 0 ) {
1148 $expand = __( 'More', 'jetpack' );
1149 } else {
1150 $expand = __( 'Share', 'jetpack' );
1151 }
1152 $parts[] = '<li><a href="#" class="sharing-anchor sd-button share-more"><span>' . $expand . '</span></a></li>';
1153 }
1154
1155 if ( 'rtl' === $dir ) {
1156 $parts = array_reverse( $parts );
1157 }
1158
1159 $sharing_content .= implode( '', $parts );
1160 $sharing_content .= '<li class="share-end"></li></ul>';
1161
1162 // Link to customization options if user can manage them.
1163 if ( current_user_can( 'manage_options' ) ) {
1164 $link_url = get_sharing_buttons_customisation_url();
1165 if ( ! empty( $link_url ) ) {
1166 $link_text = __( 'Customize buttons', 'jetpack' );
1167 $sharing_content .= '<p class="share-customize-link"><a href="' . esc_url( $link_url ) . '" target="_blank" rel="noopener noreferrer">' . esc_html( $link_text ) . '</a></p>';
1168 }
1169 }
1170
1171 if ( $count_hidden > 0 ) {
1172 $sharing_content .= '<div class="sharing-hidden"><div class="inner" style="display: none;';
1173
1174 if ( $count_hidden === 1 ) {
1175 $sharing_content .= 'width:150px;';
1176 }
1177
1178 $sharing_content .= '">';
1179
1180 if ( $count_hidden === 1 ) {
1181 $sharing_content .= '<ul style="background-image:none;">';
1182 } else {
1183 $sharing_content .= '<ul>';
1184 }
1185
1186 foreach ( $enabled['hidden'] as $service ) {
1187 // Individual HTML for sharing service.
1188 $klasses = array( 'share-' . $service->get_class() );
1189 if ( $service->is_deprecated() ) {
1190 if ( ! current_user_can( 'manage_options' ) ) {
1191 continue;
1192 }
1193 $klasses[] = 'share-deprecated';
1194 }
1195 $sharing_content .= '<li class="' . implode( ' ', $klasses ) . '">';
1196 $sharing_content .= $service->get_display( $post );
1197 $sharing_content .= '</li>';
1198 }
1199
1200 // End of wrapper.
1201 $sharing_content .= '<li class="share-end"></li></ul></div></div>';
1202 }
1203
1204 $sharing_content .= '</div></div></div>';
1205
1206 // Register our JS.
1207 if ( defined( 'JETPACK__VERSION' ) ) {
1208 $ver = JETPACK__VERSION;
1209 } else {
1210 $ver = '20211226';
1211 }
1212
1213 // @todo: Investigate if we can load this JS in the footer instead.
1214 wp_register_script(
1215 'sharing-js',
1216 Assets::get_file_url_for_environment(
1217 '_inc/build/sharedaddy/sharing.min.js',
1218 'modules/sharedaddy/sharing.js'
1219 ),
1220 array(),
1221 $ver,
1222 false
1223 );
1224
1225 // Enqueue scripts for the footer.
1226 add_action( 'wp_footer', 'sharing_add_footer' );
1227 }
1228 }
1229
1230 /**
1231 * Filters the content markup of the Jetpack sharing links
1232 *
1233 * @module sharedaddy
1234 *
1235 * @since 3.8.0
1236 * @since 6.2.0 Started sending $enabled as a second parameter.
1237 *
1238 * @param string $sharing_content Content markup of the Jetpack sharing links
1239 * @param array $enabled Array of Sharing Services currently enabled.
1240 */
1241 $sharing_markup = apply_filters( 'jetpack_sharing_display_markup', $sharing_content, $enabled );
1242
1243 if ( $echo ) {
1244 echo $text . $sharing_markup; // phpcs:ignore WordPress.Security.EscapeOutput.OutputNotEscaped
1245 } else {
1246 return $text . $sharing_markup;
1247 }
1248 }
1249
1250 /**
1251 * Get reCAPTCHA language code based off the language code of the site.
1252 *
1253 * @return string
1254 */
1255 function get_base_recaptcha_lang_code() {
1256 $base_recaptcha_lang_code_mapping = array(
1257 'en' => 'en',
1258 'nl' => 'nl',
1259 'fr' => 'fr',
1260 'fr-be' => 'fr',
1261 'fr-ca' => 'fr',
1262 'fr-ch' => 'fr',
1263 'de' => 'de',
1264 'pt' => 'pt',
1265 'pt-br' => 'pt',
1266 'ru' => 'ru',
1267 'es' => 'es',
1268 'tr' => 'tr',
1269 );
1270
1271 $blog_lang_code = get_bloginfo( 'language' );
1272 if ( isset( $base_recaptcha_lang_code_mapping[ $blog_lang_code ] ) ) {
1273 return $base_recaptcha_lang_code_mapping[ $blog_lang_code ];
1274 }
1275
1276 // if no base mapping is found return default 'en'
1277 return 'en';
1278 }
1279
1280 Sharing_Service::init();
1281