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