PluginProbe ʕ •ᴥ•ʔ
Jetpack – WP Security, Backup, Speed, & Growth / 13.2.4
Jetpack – WP Security, Backup, Speed, & Growth v13.2.4
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 2 years ago admin-sharing-rtl.css 2 years ago admin-sharing-rtl.min.css 2 years ago admin-sharing.css 2 years ago admin-sharing.js 2 years ago admin-sharing.min.css 2 years ago amp-sharing.css 2 years ago recaptcha.php 4 years ago sharedaddy.php 2 years ago sharing-service.php 2 years ago sharing-sources.php 2 years ago sharing.css 2 years ago sharing.js 3 years ago sharing.php 2 years ago
sharing-service.php
1276 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 'jetpack-whatsapp' => 'Jetpack_Share_WhatsApp',
109 'mastodon' => 'Share_Mastodon',
110 'nextdoor' => 'Share_Nextdoor',
111 'x' => 'Share_X',
112 // deprecated.
113 'skype' => 'Share_Skype',
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'] ) {
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_Source $service Sharing service.
514 *
515 * @return void
516 */
517 public function set_service( $id, Sharing_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 add_action( 'template_redirect', 'sharing_process_requests', 9 );
932
933 /**
934 * Gets the url to customise the sharing buttons in Calypso.
935 *
936 * @return string the customisation URL or null if it couldn't be determinde.
937 */
938 function get_sharing_buttons_customisation_url() {
939 return Redirect::get_url( 'calypso-marketing-sharing-buttons', array( 'site' => ( new Status() )->get_site_suffix() ) );
940 }
941
942 /**
943 * Append sharing links to text.
944 *
945 * @param string $text The original text to append sharing links onto.
946 * @param bool $echo Where to echo the text or return.
947 *
948 * @return string The original $text with, if conditions are met, the sharing links.
949 */
950 function sharing_display( $text = '', $echo = false ) {
951 global $post, $wp_current_filter;
952
953 if ( Settings::is_syncing() ) {
954 return $text;
955 }
956
957 if ( empty( $post ) ) {
958 return $text;
959 }
960
961 if ( ( is_preview() || is_admin() ) && ! ( defined( 'DOING_AJAX' ) && DOING_AJAX ) ) {
962 return $text;
963 }
964
965 // Prevent from rendering sharing buttons in block which is fetched from REST endpoint by editor
966 if ( defined( 'REST_REQUEST' ) && REST_REQUEST ) {
967 return $text;
968 }
969
970 // Do not output sharing buttons for ActivityPub requests.
971 if (
972 function_exists( '\Activitypub\is_activitypub_request' )
973 && \Activitypub\is_activitypub_request()
974 ) {
975 return $text;
976 }
977
978 // Don't output flair on excerpts.
979 if ( in_array( 'get_the_excerpt', (array) $wp_current_filter, true ) ) {
980 return $text;
981 }
982
983 // Ensure we don't display sharing buttons on post excerpts that are hooked inside the post content
984 if ( in_array( 'the_excerpt', (array) $wp_current_filter, true ) &&
985 in_array( 'the_content', (array) $wp_current_filter, true ) ) {
986 return $text;
987 }
988
989 // Don't allow flair to be added to the_content more than once (prevent infinite loops).
990 $done = false;
991 foreach ( $wp_current_filter as $filter ) {
992 if ( 'the_content' === $filter ) {
993 if ( $done ) {
994 return $text;
995 } else {
996 $done = true;
997 }
998 }
999 }
1000
1001 // check whether we are viewing the front page and whether the front page option is checked.
1002 $options = get_option( 'sharing-options' );
1003 $display_options = null;
1004
1005 if ( is_array( $options ) ) {
1006 $display_options = $options['global']['show'];
1007 }
1008
1009 if ( is_front_page() && ( is_array( $display_options ) && ! in_array( 'index', $display_options, true ) ) ) {
1010 return $text;
1011 }
1012
1013 if ( is_attachment() && in_array( 'the_excerpt', (array) $wp_current_filter, true ) ) {
1014 // Many themes run the_excerpt() conditionally on an attachment page, then run the_content().
1015 // We only want to output the sharing buttons once. Let's stick with the_content().
1016 return $text;
1017 }
1018
1019 $sharer = new Sharing_Service();
1020 $global = $sharer->get_global_options();
1021
1022 $show = false;
1023 if ( ! is_feed() ) {
1024 if ( is_singular() && in_array( get_post_type(), $global['show'], true ) ) {
1025 $show = true;
1026 } elseif ( in_array( 'index', $global['show'], true ) && ( is_home() || is_front_page() || is_archive() || is_search() || in_array( get_post_type(), $global['show'], true ) ) ) {
1027 $show = true;
1028 }
1029 }
1030
1031 /**
1032 * Filter to decide if sharing buttons should be displayed.
1033 *
1034 * @module sharedaddy
1035 *
1036 * @since 1.1.0
1037 *
1038 * @param bool $show Should the sharing buttons be displayed.
1039 * @param WP_Post $post The post to share.
1040 */
1041 $show = apply_filters( 'sharing_show', $show, $post );
1042
1043 // Disabled for this post?
1044 $switched_status = get_post_meta( $post->ID, 'sharing_disabled', false );
1045
1046 if ( ! empty( $switched_status ) ) {
1047 $show = false;
1048 }
1049
1050 // Is the post private?
1051 $post_status = get_post_status( $post->ID );
1052
1053 if ( 'private' === $post_status ) {
1054 $show = false;
1055 }
1056
1057 // Hide on password protected posts unless password is provided.
1058 if ( post_password_required( $post->ID ) ) {
1059 $show = false;
1060 }
1061
1062 /**
1063 * Filter the Sharing buttons' Ajax action name Jetpack checks for.
1064 * This allows the use of the buttons with your own Ajax implementation.
1065 *
1066 * @module sharedaddy
1067 *
1068 * @since 7.3.0
1069 *
1070 * @param string $sharing_ajax_action_name Name of the Sharing buttons' Ajax action.
1071 */
1072 $ajax_action = apply_filters( 'sharing_ajax_action', 'get_latest_posts' );
1073
1074 // Allow to be used in ajax requests for latest posts.
1075 if (
1076 defined( 'DOING_AJAX' )
1077 && DOING_AJAX
1078 && isset( $_REQUEST['action'] ) // phpcs:ignore WordPress.Security.NonceVerification.Recommended -- Nonce handling happens within each custom implementation.
1079 && $ajax_action === $_REQUEST['action'] // phpcs:ignore WordPress.Security.NonceVerification.Recommended -- Nonce handling happens within each custom implementation.
1080 ) {
1081 $show = true;
1082 }
1083
1084 $sharing_content = '';
1085 $enabled = false;
1086
1087 if ( $show ) {
1088 /**
1089 * Filters the list of enabled Sharing Services.
1090 *
1091 * @module sharedaddy
1092 *
1093 * @since 2.2.3
1094 *
1095 * @param array $sharer->get_blog_services() Array of Sharing Services currently enabled.
1096 */
1097 $enabled = apply_filters( 'sharing_enabled', $sharer->get_blog_services() );
1098
1099 if ( is_countable( $enabled['all'] ) && ( count( $enabled['all'] ) > 0 ) ) {
1100 $dir = get_option( 'text_direction' );
1101
1102 // Wrapper.
1103 $sharing_content .= '<div class="sharedaddy sd-sharing-enabled"><div class="robots-nocontent sd-block sd-social sd-social-' . $global['button_style'] . ' sd-sharing">';
1104 if ( '' !== $global['sharing_label'] ) {
1105 $sharing_content .= sprintf(
1106 /**
1107 * Filter the sharing buttons' headline structure.
1108 *
1109 * @module sharedaddy
1110 *
1111 * @since 4.4.0
1112 *
1113 * @param string $sharing_headline Sharing headline structure.
1114 * @param string $global['sharing_label'] Sharing title.
1115 * @param string $sharing Module name.
1116 */
1117 apply_filters( 'jetpack_sharing_headline_html', '<h3 class="sd-title">%s</h3>', $global['sharing_label'], 'sharing' ),
1118 esc_html( $global['sharing_label'] )
1119 );
1120 }
1121 $sharing_content .= '<div class="sd-content"><ul>';
1122
1123 // Visible items.
1124 $visible = '';
1125 foreach ( $enabled['visible'] as $service ) {
1126 $klasses = array( 'share-' . $service->get_class() );
1127 if ( $service->is_deprecated() ) {
1128 if ( ! current_user_can( 'manage_options' ) ) {
1129 continue;
1130 }
1131 $klasses[] = 'share-deprecated';
1132 }
1133 // Individual HTML for sharing service.
1134 $visible .= '<li class="' . implode( ' ', $klasses ) . '">' . $service->get_display( $post ) . '</li>';
1135 }
1136
1137 $parts = array();
1138 $parts[] = $visible;
1139 $count_hidden = is_countable( $enabled['hidden'] ) ? count( $enabled['hidden'] ) : 0;
1140 $count_visible = is_countable( $enabled['visible'] ) ? count( $enabled['visible'] ) : 0;
1141 if ( $count_hidden > 0 ) {
1142 if ( $count_visible > 0 ) {
1143 $expand = __( 'More', 'jetpack' );
1144 } else {
1145 $expand = __( 'Share', 'jetpack' );
1146 }
1147 $parts[] = '<li><a href="#" class="sharing-anchor sd-button share-more"><span>' . $expand . '</span></a></li>';
1148 }
1149
1150 if ( 'rtl' === $dir ) {
1151 $parts = array_reverse( $parts );
1152 }
1153
1154 $sharing_content .= implode( '', $parts );
1155 $sharing_content .= '<li class="share-end"></li></ul>';
1156
1157 // Link to customization options if user can manage them.
1158 if ( current_user_can( 'manage_options' ) ) {
1159 $link_url = get_sharing_buttons_customisation_url();
1160 if ( ! empty( $link_url ) ) {
1161 $link_text = __( 'Customize buttons', 'jetpack' );
1162 $sharing_content .= '<p class="share-customize-link"><a href="' . esc_url( $link_url ) . '" target="_blank" rel="noopener noreferrer">' . esc_html( $link_text ) . '</a></p>';
1163 }
1164 }
1165
1166 if ( $count_hidden > 0 ) {
1167 $sharing_content .= '<div class="sharing-hidden"><div class="inner" style="display: none;';
1168
1169 if ( $count_hidden === 1 ) {
1170 $sharing_content .= 'width:150px;';
1171 }
1172
1173 $sharing_content .= '">';
1174
1175 if ( $count_hidden === 1 ) {
1176 $sharing_content .= '<ul style="background-image:none;">';
1177 } else {
1178 $sharing_content .= '<ul>';
1179 }
1180
1181 foreach ( $enabled['hidden'] as $service ) {
1182 // Individual HTML for sharing service.
1183 $klasses = array( 'share-' . $service->get_class() );
1184 if ( $service->is_deprecated() ) {
1185 if ( ! current_user_can( 'manage_options' ) ) {
1186 continue;
1187 }
1188 $klasses[] = 'share-deprecated';
1189 }
1190 $sharing_content .= '<li class="' . implode( ' ', $klasses ) . '">';
1191 $sharing_content .= $service->get_display( $post );
1192 $sharing_content .= '</li>';
1193 }
1194
1195 // End of wrapper.
1196 $sharing_content .= '<li class="share-end"></li></ul></div></div>';
1197 }
1198
1199 $sharing_content .= '</div></div></div>';
1200
1201 // Register our JS.
1202 if ( defined( 'JETPACK__VERSION' ) ) {
1203 $ver = JETPACK__VERSION;
1204 } else {
1205 $ver = '20211226';
1206 }
1207
1208 // @todo: Investigate if we can load this JS in the footer instead.
1209 wp_register_script(
1210 'sharing-js',
1211 Assets::get_file_url_for_environment(
1212 '_inc/build/sharedaddy/sharing.min.js',
1213 'modules/sharedaddy/sharing.js'
1214 ),
1215 array(),
1216 $ver,
1217 false
1218 );
1219
1220 // Enqueue scripts for the footer.
1221 add_action( 'wp_footer', 'sharing_add_footer' );
1222 }
1223 }
1224
1225 /**
1226 * Filters the content markup of the Jetpack sharing links
1227 *
1228 * @module sharedaddy
1229 *
1230 * @since 3.8.0
1231 * @since 6.2.0 Started sending $enabled as a second parameter.
1232 *
1233 * @param string $sharing_content Content markup of the Jetpack sharing links
1234 * @param array $enabled Array of Sharing Services currently enabled.
1235 */
1236 $sharing_markup = apply_filters( 'jetpack_sharing_display_markup', $sharing_content, $enabled );
1237
1238 if ( $echo ) {
1239 echo $text . $sharing_markup; // phpcs:ignore WordPress.Security.EscapeOutput.OutputNotEscaped
1240 } else {
1241 return $text . $sharing_markup;
1242 }
1243 }
1244
1245 /**
1246 * Get reCAPTCHA language code based off the language code of the site.
1247 *
1248 * @return string
1249 */
1250 function get_base_recaptcha_lang_code() {
1251 $base_recaptcha_lang_code_mapping = array(
1252 'en' => 'en',
1253 'nl' => 'nl',
1254 'fr' => 'fr',
1255 'fr-be' => 'fr',
1256 'fr-ca' => 'fr',
1257 'fr-ch' => 'fr',
1258 'de' => 'de',
1259 'pt' => 'pt',
1260 'pt-br' => 'pt',
1261 'ru' => 'ru',
1262 'es' => 'es',
1263 'tr' => 'tr',
1264 );
1265
1266 $blog_lang_code = get_bloginfo( 'language' );
1267 if ( isset( $base_recaptcha_lang_code_mapping[ $blog_lang_code ] ) ) {
1268 return $base_recaptcha_lang_code_mapping[ $blog_lang_code ];
1269 }
1270
1271 // if no base mapping is found return default 'en'
1272 return 'en';
1273 }
1274
1275 Sharing_Service::init();
1276