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