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