PluginProbe ʕ •ᴥ•ʔ
Jetpack – WP Security, Backup, Speed, & Growth / 15.1.2
Jetpack – WP Security, Backup, Speed, & Growth v15.1.2
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 1 year ago sharedaddy.php 11 months ago sharing-service.php 11 months ago sharing-sources.php 11 months ago sharing.css 1 year ago sharing.js 3 years ago sharing.php 11 months ago
sharing-service.php
1287 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\Sync\Settings;
17
18 if ( ! defined( 'ABSPATH' ) ) {
19 exit( 0 );
20 }
21
22 require_once __DIR__ . '/sharing-sources.php';
23
24 define( 'WP_SHARING_PLUGIN_VERSION', JETPACK__VERSION );
25
26 /**
27 * Interact with a sharing service.
28 */
29 class Sharing_Service {
30 /**
31 * Should the service be available globally?
32 *
33 * @var bool
34 */
35 private $global = false;
36
37 /**
38 * Default sharing label.
39 *
40 * @var string
41 */
42 public $default_sharing_label = '';
43
44 /**
45 * Initialize the sharing service.
46 * Only run this method once upon module loading.
47 *
48 * @return void
49 */
50 public static function init() {
51 add_filter( 'the_content', 'sharing_display', 19 );
52 add_filter( 'the_excerpt', 'sharing_display', 19 );
53 }
54
55 /**
56 * Constructor.
57 */
58 public function __construct() {
59 $this->default_sharing_label = __( 'Share this:', 'jetpack' );
60 }
61
62 /**
63 * Gets a generic list of all services, without any config
64 *
65 * @return array
66 */
67 public function get_all_services_blog() {
68 $options = get_option( 'sharing-options' );
69 $all = $this->get_all_services();
70 $services = array();
71
72 foreach ( $all as $id => $name ) {
73 if ( isset( $all[ $id ] ) ) {
74 $config = array();
75
76 // Pre-load custom modules otherwise they won't know who they are
77 if ( str_starts_with( $id, 'custom-' ) && is_array( $options[ $id ] ) ) {
78 $config = $options[ $id ];
79 }
80
81 $services[ $id ] = new $all[ $id ]( $id, $config );
82 }
83 }
84
85 return $services;
86 }
87
88 /**
89 * Gets a list of all available service names and classes
90 *
91 * @param bool $include_custom Include custom sharing services.
92 *
93 * @return array
94 */
95 public function get_all_services( $include_custom = true ) {
96 // Default services
97 // if you update this list, please update the REST API tests
98 // in bin/tests/api/suites/SharingTest.php
99 $services = array(
100 'print' => 'Share_Print',
101 'email' => 'Share_Email',
102 'facebook' => 'Share_Facebook',
103 'linkedin' => 'Share_LinkedIn',
104 'reddit' => 'Share_Reddit',
105 'twitter' => 'Share_Twitter',
106 'tumblr' => 'Share_Tumblr',
107 'pinterest' => 'Share_Pinterest',
108 'pocket' => 'Share_Pocket',
109 'telegram' => 'Share_Telegram',
110 'threads' => 'Share_Threads',
111 'jetpack-whatsapp' => 'Jetpack_Share_WhatsApp',
112 'mastodon' => 'Share_Mastodon',
113 'nextdoor' => 'Share_Nextdoor',
114 'x' => 'Share_X',
115 'bluesky' => 'Share_Bluesky',
116 );
117
118 if ( is_multisite() && is_plugin_active( 'press-this/press-this-plugin.php' ) ) {
119 $services['press-this'] = 'Share_PressThis';
120 }
121
122 if ( $include_custom ) {
123 // Add any custom services in
124 $options = $this->get_global_options();
125 if ( isset( $options['custom'] ) ) {
126 foreach ( $options['custom'] as $custom_id ) {
127 $services[ $custom_id ] = 'Share_Custom';
128 }
129 }
130 }
131
132 /**
133 * Filters the list of available Sharing Services.
134 *
135 * @module sharedaddy
136 *
137 * @since 1.1.0
138 *
139 * @param array $services Array of all available Sharing Services.
140 */
141 return apply_filters( 'sharing_services', $services );
142 }
143
144 /**
145 * Save a new custom sharing service.
146 *
147 * @param string $label Service name.
148 * @param string $url Service sharing URL.
149 * @param string $icon Service icon.
150 *
151 * @return bool|Share_Custom
152 */
153 public function new_service( $label, $url, $icon ) {
154 // Validate.
155 $label = trim( wp_html_excerpt( wp_kses( $label, array() ), 30 ) );
156 $url = trim( esc_url_raw( $url ) );
157 $icon = trim( esc_url_raw( $icon ) );
158
159 if ( $label && $url && $icon ) {
160 $options = get_option( 'sharing-options' );
161 if ( ! is_array( $options ) ) {
162 $options = array();
163 }
164
165 $service_id = 'custom-' . time();
166
167 // Add a new custom service
168 $options['global']['custom'][] = $service_id;
169 if ( false !== $this->global ) {
170 $this->global['custom'][] = $service_id;
171 }
172
173 update_option( 'sharing-options', $options );
174
175 // Create a custom service and set the options for it
176 $service = new Share_Custom(
177 $service_id,
178 array(
179 'name' => $label,
180 'url' => $url,
181 'icon' => $icon,
182 )
183 );
184 $this->set_service( $service_id, $service );
185
186 // Return the service
187 return $service;
188 }
189
190 return false;
191 }
192
193 /**
194 * Delete a sharing service.
195 *
196 * @param string $service_id Service ID.
197 *
198 * @return bool
199 */
200 public function delete_service( $service_id ) {
201 $options = get_option( 'sharing-options' );
202 if ( isset( $options[ $service_id ] ) ) {
203 unset( $options[ $service_id ] );
204 }
205
206 $key = array_search( $service_id, $options['global']['custom'], true );
207 if ( $key !== false ) {
208 unset( $options['global']['custom'][ $key ] );
209 }
210
211 update_option( 'sharing-options', $options );
212 return true;
213 }
214
215 /**
216 * Save enabled sharing services.
217 *
218 * @param array $visible Visible sharing services.
219 * @param array $hidden Hidden sharing services (available under a dropdown).
220 *
221 * @return bool
222 */
223 public function set_blog_services( array $visible, array $hidden ) {
224 $services = $this->get_all_services();
225 // Validate the services
226 $available = array_keys( $services );
227
228 // Only allow services that we have defined
229 $hidden = array_intersect( $hidden, $available );
230 $visible = array_intersect( $visible, $available );
231
232 // Ensure we don't have the same ones in hidden and visible
233 $hidden = array_diff( $hidden, $visible );
234
235 /**
236 * Control the state of the list of sharing services.
237 *
238 * @module sharedaddy
239 *
240 * @since 1.1.0
241 *
242 * @param array $args {
243 * Array of options describing the state of the sharing services.
244 *
245 * @type array $services List of all available service names and classes.
246 * @type array $available Validated list of all available service names and classes.
247 * @type array $hidden List of services hidden behind a "More" button.
248 * @type array $visible List of visible services.
249 * @type array $this->get_blog_services() Array of Sharing Services currently enabled.
250 * }
251 */
252 do_action(
253 'sharing_get_services_state',
254 array(
255 'services' => $services,
256 'available' => $available,
257 'hidden' => $hidden,
258 'visible' => $visible,
259 'currently_enabled' => $this->get_blog_services(),
260 )
261 );
262
263 return update_option(
264 'sharing-services',
265 array(
266 'visible' => $visible,
267 'hidden' => $hidden,
268 )
269 );
270 }
271
272 /**
273 * Get information about enabled sharing services on the site.
274 *
275 * @return array
276 */
277 public function get_blog_services() {
278 $options = get_option( 'sharing-options' );
279 $enabled = get_option( 'sharing-services' );
280 $services = $this->get_all_services();
281
282 /**
283 * Check if options exist and are well formatted.
284 * This avoids issues on sites with corrupted options.
285 *
286 * @see https://github.com/Automattic/jetpack/issues/6121
287 */
288 if ( ! is_array( $options ) || ! isset( $options['button_style'] ) || ! isset( $options['global'] ) ) {
289 $global_options = array( 'global' => $this->get_global_options() );
290 $options = is_array( $options )
291 ? array_merge( $options, $global_options )
292 : $global_options;
293 }
294
295 $global = $options['global'];
296
297 // Default services
298 if ( ! is_array( $enabled ) ) {
299 $enabled = array(
300 'visible' => array(
301 'facebook',
302 'x',
303 ),
304 'hidden' => array(),
305 );
306
307 /**
308 * Filters the list of default Sharing Services.
309 *
310 * @module sharedaddy
311 *
312 * @since 1.1.0
313 *
314 * @param array $enabled Array of default Sharing Services.
315 */
316 $enabled = apply_filters( 'sharing_default_services', $enabled );
317 }
318
319 // Cleanup after any filters that may have produced duplicate services
320 if ( isset( $enabled['visible'] ) && is_array( $enabled['visible'] ) ) {
321 $enabled['visible'] = array_unique( $enabled['visible'] );
322 } else {
323 $enabled['visible'] = array();
324 }
325
326 if ( isset( $enabled['hidden'] ) && is_array( $enabled['hidden'] ) ) {
327 $enabled['hidden'] = array_unique( $enabled['hidden'] );
328 } else {
329 $enabled['hidden'] = array();
330 }
331
332 // Form the enabled services
333 $blog = array(
334 'visible' => array(),
335 'hidden' => array(),
336 );
337
338 foreach ( $blog as $area => $stuff ) {
339 foreach ( (array) $enabled[ $area ] as $service ) {
340 if ( isset( $services[ $service ] ) ) {
341 if ( ! isset( $options[ $service ] ) || ! is_array( $options[ $service ] ) ) {
342 $options[ $service ] = array();
343 }
344 $blog[ $area ][ $service ] = new $services[ $service ]( $service, array_merge( $global, $options[ $service ] ) );
345 }
346 }
347 }
348
349 /**
350 * Filters the list of enabled Sharing Services.
351 *
352 * @module sharedaddy
353 *
354 * @since 1.1.0
355 *
356 * @param array $blog Array of enabled Sharing Services.
357 */
358 $blog = apply_filters( 'sharing_services_enabled', $blog );
359
360 // Add CSS for NASCAR
361 if ( ( is_countable( $blog['visible'] ) && count( $blog['visible'] ) ) || ( is_countable( $blog['hidden'] ) && count( $blog['hidden'] ) ) ) {
362 add_filter( 'post_flair_block_css', 'post_flair_service_enabled_sharing' );
363 }
364
365 // Convenience for checking if a service is present
366 $blog['all'] = array_flip( array_merge( array_keys( $blog['visible'] ), array_keys( $blog['hidden'] ) ) );
367 return $blog;
368 }
369
370 /**
371 * Get information about a specific enabled sharing service.
372 *
373 * @param string $service_name Service name.
374 *
375 * @return bool|Sharing_Source
376 */
377 public function get_service( $service_name ) {
378 $services = $this->get_blog_services();
379
380 if ( isset( $services['visible'][ $service_name ] ) ) {
381 return $services['visible'][ $service_name ];
382 }
383
384 if ( isset( $services['hidden'][ $service_name ] ) ) {
385 return $services['hidden'][ $service_name ];
386 }
387
388 return false;
389 }
390
391 /**
392 * Update global sharing options.
393 *
394 * @param array $data Array of new sharing options to save.
395 */
396 public function set_global_options( $data ) {
397 $options = get_option( 'sharing-options' );
398
399 // No options yet.
400 if ( ! is_array( $options ) ) {
401 $options = array();
402 }
403
404 // Defaults.
405 $options['global'] = array(
406 'button_style' => 'icon-text',
407 'sharing_label' => $this->default_sharing_label,
408 'open_links' => 'same',
409 'show' => ! isset( $options['global'] ) ? array( 'post', 'page' ) : array(),
410 'custom' => isset( $options['global']['custom'] ) ? $options['global']['custom'] : array(),
411 );
412
413 /**
414 * Filters global sharing settings.
415 *
416 * @module sharedaddy
417 *
418 * @since 1.1.0
419 *
420 * @param array $options['global'] Array of global sharing settings.
421 */
422 $options['global'] = apply_filters( 'sharing_default_global', $options['global'] );
423
424 // Validate options and set from our data
425 if (
426 isset( $data['button_style'] )
427 && in_array( $data['button_style'], array( 'icon-text', 'icon', 'text', 'official' ), true )
428 ) {
429 $options['global']['button_style'] = $data['button_style'];
430 }
431
432 if ( isset( $data['sharing_label'] ) ) {
433 if ( $this->default_sharing_label === $data['sharing_label'] ) {
434 $options['global']['sharing_label'] = false;
435 } else {
436 $options['global']['sharing_label'] = trim( wp_kses( stripslashes( $data['sharing_label'] ), array() ) );
437 }
438 }
439
440 if (
441 isset( $data['open_links'] )
442 && in_array( $data['open_links'], array( 'new', 'same' ), true )
443 ) {
444 $options['global']['open_links'] = $data['open_links'];
445 }
446
447 $shows = array_values( get_post_types( array( 'public' => true ) ) );
448 $shows[] = 'index';
449 if ( isset( $data['show'] ) ) {
450 if ( is_scalar( $data['show'] ) ) {
451 switch ( $data['show'] ) {
452 case 'posts':
453 $data['show'] = array( 'post', 'page' );
454 break;
455 case 'index':
456 $data['show'] = array( 'index' );
457 break;
458 case 'posts-index':
459 $data['show'] = array( 'post', 'page', 'index' );
460 break;
461 }
462 }
463
464 $data['show'] = array_intersect( $data['show'], $shows );
465 if ( $data['show'] ) {
466 $options['global']['show'] = $data['show'];
467 }
468 }
469
470 update_option( 'sharing-options', $options );
471 return $options['global'];
472 }
473
474 /**
475 * Get global sharing options for the site.
476 *
477 * @return array
478 */
479 public function get_global_options() {
480 if ( $this->global === false ) {
481 $options = get_option( 'sharing-options' );
482
483 if ( is_array( $options ) && isset( $options['global'] ) && is_array( $options['global'] ) ) {
484 $this->global = $options['global'];
485 } else {
486 $this->global = $this->set_global_options( $options );
487 }
488 }
489
490 if ( ! isset( $this->global['show'] ) ) {
491 $this->global['show'] = array( 'post', 'page' );
492 } elseif ( is_scalar( $this->global['show'] ) ) {
493 switch ( $this->global['show'] ) {
494 case 'posts':
495 $this->global['show'] = array( 'post', 'page' );
496 break;
497 case 'index':
498 $this->global['show'] = array( 'index' );
499 break;
500 case 'posts-index':
501 $this->global['show'] = array( 'post', 'page', 'index' );
502 break;
503 }
504 }
505
506 if ( ! isset( $this->global['sharing_label'] ) || false === $this->global['sharing_label'] || $this->global['sharing_label'] === 'Share this:' ) {
507 $this->global['sharing_label'] = $this->default_sharing_label;
508 }
509
510 return $this->global;
511 }
512
513 /**
514 * Save a sharing service for use.
515 *
516 * @param int $id Sharing unique ID.
517 * @param Sharing_Advanced_Source $service Sharing service.
518 *
519 * @return void
520 */
521 public function set_service( $id, Sharing_Advanced_Source $service ) {
522 // Update the options for this service
523 $options = get_option( 'sharing-options' );
524
525 // No options yet
526 if ( ! is_array( $options ) ) {
527 $options = array();
528 }
529
530 /**
531 * Get the state of a sharing button.
532 *
533 * @module sharedaddy
534 *
535 * @since 1.1.0
536 *
537 * @param array $args {
538 * State of a sharing button.
539 *
540 * @type string $id Service ID.
541 * @type array $options Array of all sharing options.
542 * @type array $service Details about a service.
543 * }
544 */
545 do_action(
546 'sharing_get_button_state',
547 array(
548 'id' => $id,
549 'options' => $options,
550 'service' => $service,
551 )
552 );
553
554 $options[ $id ] = $service->get_options();
555
556 update_option( 'sharing-options', array_filter( $options ) );
557 }
558
559 /**
560 * Get stats for a site, a post, or a sharing service.
561 * Soon to come to a .org plugin near you!
562 *
563 * @param string|bool $service_name Service name.
564 * @param int|bool $post_id Post ID.
565 * @param int|bool $_blog_id Blog ID.
566 *
567 * @return int
568 */
569 public function get_total( $service_name = false, $post_id = false, $_blog_id = false ) {
570 global $wpdb, $blog_id;
571 if ( ! $_blog_id ) {
572 $_blog_id = $blog_id;
573 }
574 if ( $service_name === false ) {
575 if ( $post_id > 0 ) {
576 // total number of shares for this post
577 $sql = $wpdb->prepare( 'SELECT SUM( count ) FROM sharing_stats WHERE blog_id = %d AND post_id = %d', $_blog_id, $post_id );
578 $cache_key = "sharing_service_get_total_b{$_blog_id}_p{$post_id}";
579 } else {
580 // total number of shares for this blog
581 $sql = $wpdb->prepare( 'SELECT SUM( count ) FROM sharing_stats WHERE blog_id = %d', $_blog_id );
582 $cache_key = "sharing_service_get_total_b{$_blog_id}";
583 }
584 } elseif ( $post_id > 0 ) {
585 $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 );
586 $cache_key = "sharing_service_get_total_b{$_blog_id}_p{$post_id}_s{$service_name}";
587 } else {
588 $sql = $wpdb->prepare( 'SELECT SUM( count ) FROM sharing_stats WHERE blog_id = %d AND share_service = %s', $_blog_id, $service_name );
589 $cache_key = "sharing_service_get_total_b{$_blog_id}_s{$service_name}";
590 }
591
592 $ret = wp_cache_get( $cache_key, 'sharing' );
593 if ( $ret === false ) {
594 // phpcs:ignore WordPress.DB.PreparedSQL.NotPrepared, WordPress.DB.DirectDatabaseQuery.DirectQuery -- Prepared above.
595 $ret = (int) $wpdb->get_var( $sql );
596 wp_cache_set( $cache_key, $ret, 'sharing', 5 * MINUTE_IN_SECONDS );
597 }
598 return $ret;
599 }
600
601 /**
602 * Get total stats for a site, for all sharing services.
603 *
604 * @param int|bool $post_id Post ID.
605 *
606 * @return array
607 */
608 public function get_services_total( $post_id = false ) {
609 $totals = array();
610 $services = $this->get_blog_services();
611
612 if ( ! empty( $services ) && isset( $services['all'] ) ) {
613 foreach ( $services['all'] as $key => $value ) {
614 $totals[ $key ] = new Sharing_Service_Total( $key, $this->get_total( $key, $post_id ) );
615 }
616 }
617 usort( $totals, array( 'Sharing_Service_Total', 'cmp' ) );
618
619 return $totals;
620 }
621
622 /**
623 * Get sharing stats for all posts on the site.
624 *
625 * @return array
626 */
627 public function get_posts_total() {
628 $totals = array();
629 global $wpdb, $blog_id;
630
631 $cache_key = "sharing_service_get_posts_total_{$blog_id}";
632 $my_data = wp_cache_get( $cache_key, 'sharing' );
633 if ( $my_data === false ) {
634 $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
635 wp_cache_set( $cache_key, $my_data, 'sharing', 5 * MINUTE_IN_SECONDS );
636 }
637
638 if ( ! empty( $my_data ) ) {
639 foreach ( $my_data as $row ) {
640 $totals[] = new Sharing_Post_Total( $row->id, $row->total );
641 }
642 }
643
644 usort( $totals, array( 'Sharing_Post_Total', 'cmp' ) );
645
646 return $totals;
647 }
648 }
649
650 /**
651 * Get stats for a specific sharing service.
652 */
653 class Sharing_Service_Total {
654 /**
655 * Sharing service ID.
656 *
657 * @var int
658 */
659 public $id = '';
660
661 /**
662 * Service name.
663 *
664 * @var string
665 */
666 public $name = '';
667
668 /**
669 * Sharing service name.
670 *
671 * @var string
672 */
673 public $service = '';
674
675 /**
676 * Total number of shares for this service.
677 *
678 * @var string
679 */
680 public $total = 0;
681
682 /**
683 * Constructor.
684 *
685 * @param int $id Service ID.
686 * @param int $total Total shares.
687 */
688 public function __construct( $id, $total ) {
689 $services = new Sharing_Service();
690 $this->id = esc_html( $id );
691 $this->service = $services->get_service( $id );
692 $this->total = (int) $total;
693
694 if ( $this->service instanceof Sharing_Source ) {
695 $this->name = $this->service->get_name();
696 }
697 }
698
699 /**
700 * Compare total shares between 2 posts.
701 *
702 * @param object $a Sharing_Service_Total object.
703 * @param object $b Sharing_Service_Total object.
704 *
705 * @return int -1, 0, or 1 if $a is <, =, or > $b
706 */
707 public static function cmp( $a, $b ) {
708 if ( $a->total === $b->total ) {
709 return $b->name <=> $a->name;
710 }
711 return $b->total <=> $a->total;
712 }
713 }
714
715 /**
716 * Get sharing stats for a specific post.
717 */
718 class Sharing_Post_Total {
719 /**
720 * Sharing service ID.
721 *
722 * @var int
723 */
724 public $id = 0;
725
726 /**
727 * Total shares.
728 *
729 * @var int
730 */
731 public $total = 0;
732
733 /**
734 * Post title.
735 *
736 * @var string
737 */
738 public $title = '';
739
740 /**
741 * Post permalink.
742 *
743 * @var string
744 */
745 public $url = '';
746
747 /**
748 * Constructor.
749 *
750 * @param int $id Service ID.
751 * @param int $total Total shares.
752 */
753 public function __construct( $id, $total ) {
754 $this->id = (int) $id;
755 $this->total = (int) $total;
756 $this->title = get_the_title( $this->id );
757 $this->url = get_permalink( $this->id );
758 }
759
760 /**
761 * Compare total shares between 2 posts.
762 *
763 * @param object $a Sharing_Post_Total object.
764 * @param object $b Sharing_Post_Total object.
765 *
766 * @return int -1, 0, or 1 if $a is <, =, or > $b
767 */
768 public static function cmp( $a, $b ) {
769 if ( $a->total === $b->total ) {
770 return $b->id <=> $a->id;
771 }
772 return $b->total <=> $a->total;
773 }
774 }
775
776 /**
777 * Populate sharing counts global with a post we want to count shares for.
778 *
779 * @param int $post_id Post ID.
780 *
781 * @return void
782 */
783 function sharing_register_post_for_share_counts( $post_id ) {
784 global $jetpack_sharing_counts;
785
786 if ( ! isset( $jetpack_sharing_counts ) || ! is_array( $jetpack_sharing_counts ) ) {
787 $jetpack_sharing_counts = array();
788 }
789
790 $jetpack_sharing_counts[ (int) $post_id ] = get_permalink( $post_id );
791 }
792
793 /**
794 * Determine whether we should load sharing scripts or not.
795 *
796 * @return bool
797 */
798 function sharing_maybe_enqueue_scripts() {
799 $sharer = new Sharing_Service();
800 $global_options = $sharer->get_global_options();
801
802 $enqueue = false;
803 if ( is_singular() && in_array( get_post_type(), $global_options['show'], true ) ) {
804 $enqueue = true;
805 } elseif (
806 in_array( 'index', $global_options['show'], true )
807 && (
808 is_home()
809 || is_front_page()
810 || is_archive()
811 || is_search()
812 || in_array( get_post_type(), $global_options['show'], true )
813 )
814 ) {
815 $enqueue = true;
816 }
817
818 /**
819 * Filter to decide when sharing scripts should be enqueued.
820 *
821 * @module sharedaddy
822 *
823 * @since 3.2.0
824 *
825 * @param bool $enqueue Decide if the sharing scripts should be enqueued.
826 */
827 return (bool) apply_filters( 'sharing_enqueue_scripts', $enqueue );
828 }
829
830 /**
831 * Add sharing JavaScript to the footer of a page.
832 *
833 * @return void
834 */
835 function sharing_add_footer() {
836 if (
837 class_exists( 'Jetpack_AMP_Support' )
838 && Jetpack_AMP_Support::is_amp_request()
839 ) {
840 return;
841 }
842
843 global $jetpack_sharing_counts;
844
845 if (
846 /**
847 * Filter all JavaScript output by the sharing module.
848 *
849 * @module sharedaddy
850 *
851 * @since 1.1.0
852 *
853 * @param bool true Control whether the sharing module should add any JavaScript to the site. Default to true.
854 */
855 apply_filters( 'sharing_js', true )
856 && sharing_maybe_enqueue_scripts()
857 ) {
858 if (
859 /**
860 * Filter the display of sharing counts next to the sharing buttons.
861 *
862 * @module sharedaddy
863 *
864 * @since 3.2.0
865 *
866 * @param bool true Control the display of counters next to the sharing buttons. Default to true.
867 */
868 apply_filters( 'jetpack_sharing_counts', true )
869 && is_array( $jetpack_sharing_counts )
870 && count( $jetpack_sharing_counts )
871 ) :
872 $sharing_post_urls = array_filter( $jetpack_sharing_counts );
873 if ( $sharing_post_urls ) :
874 ?>
875
876 <script type="text/javascript">
877 window.WPCOM_sharing_counts = <?php echo wp_json_encode( array_flip( $sharing_post_urls ) ); ?>;
878 </script>
879 <?php
880 endif;
881 endif;
882
883 wp_enqueue_script( 'sharing-js' );
884 $sharing_js_options = array(
885 'lang' => get_base_recaptcha_lang_code(),
886 /** This filter is documented in modules/sharedaddy/sharing-service.php */
887 'counts' => apply_filters( 'jetpack_sharing_counts', true ),
888 'is_stats_active' => Jetpack::is_module_active( 'stats' ),
889 );
890 wp_localize_script( 'sharing-js', 'sharing_js_options', $sharing_js_options );
891 }
892 $sharer = new Sharing_Service();
893 $enabled = $sharer->get_blog_services();
894 foreach ( array_merge( $enabled['visible'], $enabled['hidden'] ) as $service ) {
895 $service->display_footer();
896 }
897 }
898
899 /**
900 * Enqueue sharing CSS in head.
901 *
902 * @return void
903 */
904 function sharing_add_header() {
905 $sharer = new Sharing_Service();
906 $enabled = $sharer->get_blog_services();
907
908 foreach ( array_merge( $enabled['visible'], $enabled['hidden'] ) as $service ) {
909 $service->display_header();
910 }
911
912 if ( is_countable( $enabled['all'] ) && ( count( $enabled['all'] ) > 0 ) && sharing_maybe_enqueue_scripts() ) {
913 wp_enqueue_style( 'sharedaddy', plugin_dir_url( __FILE__ ) . 'sharing.css', array(), JETPACK__VERSION );
914 wp_enqueue_style( 'social-logos' );
915 }
916 }
917 add_action( 'wp_head', 'sharing_add_header', 1 );
918
919 /**
920 * Launch sharing requests on page load when a specific query string is used.
921 *
922 * @return void
923 */
924 function sharing_process_requests() {
925 global $post;
926
927 // Only process if: single post and share=X defined
928 if ( ( is_page() || is_single() ) && isset( $_GET['share'] ) && is_string( $_GET['share'] ) ) { // phpcs:ignore WordPress.Security.NonceVerification.Recommended
929 $sharer = new Sharing_Service();
930
931 $service = $sharer->get_service( sanitize_text_field( wp_unslash( $_GET['share'] ) ) ); // phpcs:ignore WordPress.Security.NonceVerification.Recommended
932 if ( $service ) {
933 $service->process_request( $post, $_POST ); // phpcs:ignore WordPress.Security.NonceVerification.Missing
934 }
935 }
936 }
937
938 // phpcs:ignore WordPress.Security.NonceVerification.Recommended -- Only checking for the data being present.
939 if ( isset( $_GET['share'] ) ) {
940 add_action( 'template_redirect', 'sharing_process_requests', 9 );
941 }
942
943 /**
944 * Gets the url to customise the sharing buttons in WP-Admin.
945 *
946 * @return string the customisation URL.
947 */
948 function get_sharing_buttons_customisation_url() {
949 return admin_url( 'options-general.php?page=sharing' );
950 }
951
952 /**
953 * Append sharing links to text.
954 *
955 * @param string $text The original text to append sharing links onto.
956 * @param bool $echo Where to echo the text or return.
957 *
958 * @return string The original $text with, if conditions are met, the sharing links.
959 */
960 function sharing_display( $text = '', $echo = false ) {
961 global $post, $wp_current_filter;
962
963 if ( Settings::is_syncing() ) {
964 return $text;
965 }
966
967 // We require the post to not be empty and be an actual WordPress post object. If it's not - we just return.
968 if ( empty( $post ) || ! $post instanceof \WP_Post ) {
969 return $text;
970 }
971
972 if ( ( is_preview() || is_admin() ) && ! ( defined( 'DOING_AJAX' ) && DOING_AJAX ) ) {
973 return $text;
974 }
975
976 // Prevent from rendering sharing buttons in block which is fetched from REST endpoint by editor
977 if ( defined( 'REST_REQUEST' ) && REST_REQUEST ) {
978 return $text;
979 }
980
981 // Do not output sharing buttons for ActivityPub requests.
982 if (
983 function_exists( '\Activitypub\is_activitypub_request' )
984 && \Activitypub\is_activitypub_request()
985 ) {
986 return $text;
987 }
988
989 // Don't output flair on excerpts.
990 if ( in_array( 'get_the_excerpt', (array) $wp_current_filter, true ) ) {
991 return $text;
992 }
993
994 // Ensure we don't display sharing buttons on post excerpts that are hooked inside the post content
995 if ( in_array( 'the_excerpt', (array) $wp_current_filter, true ) &&
996 in_array( 'the_content', (array) $wp_current_filter, true ) ) {
997 return $text;
998 }
999
1000 // Don't allow flair to be added to the_content more than once (prevent infinite loops).
1001 $done = false;
1002 foreach ( $wp_current_filter as $filter ) {
1003 if ( 'the_content' === $filter ) {
1004 if ( $done ) {
1005 return $text;
1006 } else {
1007 $done = true;
1008 }
1009 }
1010 }
1011
1012 // check whether we are viewing the front page and whether the front page option is checked.
1013 $options = get_option( 'sharing-options' );
1014 $display_options = null;
1015
1016 if ( is_array( $options ) ) {
1017 $display_options = $options['global']['show'];
1018 }
1019
1020 if ( is_front_page() && ( is_array( $display_options ) && ! in_array( 'index', $display_options, true ) ) ) {
1021 return $text;
1022 }
1023
1024 if ( is_attachment() && in_array( 'the_excerpt', (array) $wp_current_filter, true ) ) {
1025 // Many themes run the_excerpt() conditionally on an attachment page, then run the_content().
1026 // We only want to output the sharing buttons once. Let's stick with the_content().
1027 return $text;
1028 }
1029
1030 $sharer = new Sharing_Service();
1031 $global = $sharer->get_global_options();
1032
1033 $show = false;
1034 if ( ! is_feed() ) {
1035 if ( is_singular() && in_array( get_post_type(), $global['show'], true ) ) {
1036 $show = true;
1037 } elseif ( in_array( 'index', $global['show'], true ) && ( is_home() || is_front_page() || is_archive() || is_search() || in_array( get_post_type(), $global['show'], true ) ) ) {
1038 $show = true;
1039 }
1040 }
1041
1042 /**
1043 * Filter to decide if sharing buttons should be displayed.
1044 *
1045 * @module sharedaddy
1046 *
1047 * @since 1.1.0
1048 *
1049 * @param bool $show Should the sharing buttons be displayed.
1050 * @param WP_Post $post The post to share.
1051 */
1052 $show = apply_filters( 'sharing_show', $show, $post );
1053
1054 // Disabled for this post?
1055 $switched_status = get_post_meta( $post->ID, 'sharing_disabled', false );
1056
1057 if ( ! empty( $switched_status ) ) {
1058 $show = false;
1059 }
1060
1061 // Is the post private?
1062 $post_status = get_post_status( $post->ID );
1063
1064 if ( 'private' === $post_status ) {
1065 $show = false;
1066 }
1067
1068 // Hide on password protected posts unless password is provided.
1069 if ( post_password_required( $post->ID ) ) {
1070 $show = false;
1071 }
1072
1073 /**
1074 * Filter the Sharing buttons' Ajax action name Jetpack checks for.
1075 * This allows the use of the buttons with your own Ajax implementation.
1076 *
1077 * @module sharedaddy
1078 *
1079 * @since 7.3.0
1080 *
1081 * @param string $sharing_ajax_action_name Name of the Sharing buttons' Ajax action.
1082 */
1083 $ajax_action = apply_filters( 'sharing_ajax_action', 'get_latest_posts' );
1084
1085 // Allow to be used in ajax requests for latest posts.
1086 if (
1087 defined( 'DOING_AJAX' )
1088 && DOING_AJAX
1089 && isset( $_REQUEST['action'] ) // phpcs:ignore WordPress.Security.NonceVerification.Recommended -- Nonce handling happens within each custom implementation.
1090 && $ajax_action === $_REQUEST['action'] // phpcs:ignore WordPress.Security.NonceVerification.Recommended -- Nonce handling happens within each custom implementation.
1091 ) {
1092 $show = true;
1093 }
1094
1095 $sharing_content = '';
1096 $enabled = false;
1097
1098 if ( $show ) {
1099 /**
1100 * Filters the list of enabled Sharing Services.
1101 *
1102 * @module sharedaddy
1103 *
1104 * @since 2.2.3
1105 *
1106 * @param array $sharer->get_blog_services() Array of Sharing Services currently enabled.
1107 */
1108 $enabled = apply_filters( 'sharing_enabled', $sharer->get_blog_services() );
1109
1110 if ( is_countable( $enabled['all'] ) && ( count( $enabled['all'] ) > 0 ) ) {
1111 $dir = get_option( 'text_direction' );
1112
1113 // Wrapper.
1114 $sharing_content .= '<div class="sharedaddy sd-sharing-enabled"><div class="robots-nocontent sd-block sd-social sd-social-' . ( $global['button_style'] ?? 'icon-text' ) . ' sd-sharing">';
1115 if ( '' !== $global['sharing_label'] ) {
1116 $sharing_content .= sprintf(
1117 /**
1118 * Filter the sharing buttons' headline structure.
1119 *
1120 * @module sharedaddy
1121 *
1122 * @since 4.4.0
1123 *
1124 * @param string $sharing_headline Sharing headline structure.
1125 * @param string $global['sharing_label'] Sharing title.
1126 * @param string $sharing Module name.
1127 */
1128 apply_filters( 'jetpack_sharing_headline_html', '<h3 class="sd-title">%s</h3>', $global['sharing_label'], 'sharing' ),
1129 esc_html( $global['sharing_label'] )
1130 );
1131 }
1132 $sharing_content .= '<div class="sd-content"><ul>';
1133
1134 // Visible items.
1135 $visible = '';
1136 foreach ( $enabled['visible'] as $service ) {
1137 $klasses = array( 'share-' . $service->get_class() );
1138 if ( $service->is_deprecated() ) {
1139 if ( ! current_user_can( 'manage_options' ) ) {
1140 continue;
1141 }
1142 $klasses[] = 'share-deprecated';
1143 }
1144 // Individual HTML for sharing service.
1145 $visible .= '<li class="' . implode( ' ', $klasses ) . '">' . $service->get_display( $post ) . '</li>';
1146 }
1147
1148 $parts = array();
1149 $parts[] = $visible;
1150 $count_hidden = is_countable( $enabled['hidden'] ) ? count( $enabled['hidden'] ) : 0;
1151 $count_visible = is_countable( $enabled['visible'] ) ? count( $enabled['visible'] ) : 0;
1152 if ( $count_hidden > 0 ) {
1153 if ( $count_visible > 0 ) {
1154 $expand = __( 'More', 'jetpack' );
1155 } else {
1156 $expand = __( 'Share', 'jetpack' );
1157 }
1158 $parts[] = '<li><a href="#" class="sharing-anchor sd-button share-more"><span>' . $expand . '</span></a></li>';
1159 }
1160
1161 if ( 'rtl' === $dir ) {
1162 $parts = array_reverse( $parts );
1163 }
1164
1165 $sharing_content .= implode( '', $parts );
1166 $sharing_content .= '<li class="share-end"></li></ul>';
1167
1168 // Link to customization options if user can manage them.
1169 if ( current_user_can( 'manage_options' ) ) {
1170 $link_url = get_sharing_buttons_customisation_url();
1171 if ( ! empty( $link_url ) ) {
1172 $link_text = __( 'Customize buttons', 'jetpack' );
1173 $sharing_content .= '<p class="share-customize-link"><a href="' . esc_url( $link_url ) . '" target="_blank" rel="noopener noreferrer">' . esc_html( $link_text ) . '</a></p>';
1174 }
1175 }
1176
1177 if ( $count_hidden > 0 ) {
1178 $sharing_content .= '<div class="sharing-hidden"><div class="inner" style="display: none;';
1179
1180 if ( $count_hidden === 1 ) {
1181 $sharing_content .= 'width:150px;';
1182 }
1183
1184 $sharing_content .= '">';
1185
1186 if ( $count_hidden === 1 ) {
1187 $sharing_content .= '<ul style="background-image:none;">';
1188 } else {
1189 $sharing_content .= '<ul>';
1190 }
1191
1192 foreach ( $enabled['hidden'] as $service ) {
1193 // Individual HTML for sharing service.
1194 $klasses = array( 'share-' . $service->get_class() );
1195 if ( $service->is_deprecated() ) {
1196 if ( ! current_user_can( 'manage_options' ) ) {
1197 continue;
1198 }
1199 $klasses[] = 'share-deprecated';
1200 }
1201 $sharing_content .= '<li class="' . implode( ' ', $klasses ) . '">';
1202 $sharing_content .= $service->get_display( $post );
1203 $sharing_content .= '</li>';
1204 }
1205
1206 // End of wrapper.
1207 $sharing_content .= '<li class="share-end"></li></ul></div></div>';
1208 }
1209
1210 $sharing_content .= '</div></div></div>';
1211
1212 // Register our JS.
1213 if ( defined( 'JETPACK__VERSION' ) ) {
1214 $ver = JETPACK__VERSION;
1215 } else {
1216 $ver = '20211226';
1217 }
1218
1219 // @todo: Investigate if we can load this JS in the footer instead.
1220 wp_register_script(
1221 'sharing-js',
1222 Assets::get_file_url_for_environment(
1223 '_inc/build/sharedaddy/sharing.min.js',
1224 'modules/sharedaddy/sharing.js'
1225 ),
1226 array(),
1227 $ver,
1228 false
1229 );
1230
1231 // Enqueue scripts for the footer.
1232 add_action( 'wp_footer', 'sharing_add_footer' );
1233 }
1234 }
1235
1236 /**
1237 * Filters the content markup of the Jetpack sharing links
1238 *
1239 * @module sharedaddy
1240 *
1241 * @since 3.8.0
1242 * @since 6.2.0 Started sending $enabled as a second parameter.
1243 *
1244 * @param string $sharing_content Content markup of the Jetpack sharing links
1245 * @param array $enabled Array of Sharing Services currently enabled.
1246 */
1247 $sharing_markup = apply_filters( 'jetpack_sharing_display_markup', $sharing_content, $enabled );
1248
1249 if ( $echo ) {
1250 echo $text . $sharing_markup; // phpcs:ignore WordPress.Security.EscapeOutput.OutputNotEscaped
1251 } else {
1252 return $text . $sharing_markup;
1253 }
1254 }
1255
1256 /**
1257 * Get reCAPTCHA language code based off the language code of the site.
1258 *
1259 * @return string
1260 */
1261 function get_base_recaptcha_lang_code() {
1262 $base_recaptcha_lang_code_mapping = array(
1263 'en' => 'en',
1264 'nl' => 'nl',
1265 'fr' => 'fr',
1266 'fr-be' => 'fr',
1267 'fr-ca' => 'fr',
1268 'fr-ch' => 'fr',
1269 'de' => 'de',
1270 'pt' => 'pt',
1271 'pt-br' => 'pt',
1272 'ru' => 'ru',
1273 'es' => 'es',
1274 'tr' => 'tr',
1275 );
1276
1277 $blog_lang_code = get_bloginfo( 'language' );
1278 if ( isset( $base_recaptcha_lang_code_mapping[ $blog_lang_code ] ) ) {
1279 return $base_recaptcha_lang_code_mapping[ $blog_lang_code ];
1280 }
1281
1282 // if no base mapping is found return default 'en'
1283 return 'en';
1284 }
1285
1286 Sharing_Service::init();
1287