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