if ( ! isset( $this->global['sharing_label'] ) || false === $this->global['sharing_label'] || $this->global['sharing_label'] === 'Share this:' ) {
$this->global['sharing_label'] = $this->default_sharing_label;
* Save a sharing service for use.
* @param int $id Sharing unique ID.
* @param Sharing_Advanced_Source $service Sharing service.
public function set_service( $id, Sharing_Advanced_Source $service ) {
// Update the options for this service
$options = get_option( 'sharing-options' );
if ( ! is_array( $options ) ) {
* Get the state of a sharing button.
* State of a sharing button.
* @type string $id Service ID.
* @type array $options Array of all sharing options.
* @type array $service Details about a service.
'sharing_get_button_state',
$options[ $id ] = $service->get_options();
update_option( 'sharing-options', array_filter( $options ) );
* Get stats for a site, a post, or a sharing service.
* Soon to come to a .org plugin near you!
* @param string|bool $service_name Service name.
* @param int|bool $post_id Post ID.
* @param int|bool $_blog_id Blog ID.
public function get_total( $service_name = false, $post_id = false, $_blog_id = false ) {
if ( $service_name === false ) {
// total number of shares for this post
$sql = $wpdb->prepare( 'SELECT SUM( count ) FROM sharing_stats WHERE blog_id = %d AND post_id = %d', $_blog_id, $post_id );
$cache_key = "sharing_service_get_total_b{$_blog_id}_p{$post_id}";
// total number of shares for this blog
$sql = $wpdb->prepare( 'SELECT SUM( count ) FROM sharing_stats WHERE blog_id = %d', $_blog_id );
$cache_key = "sharing_service_get_total_b{$_blog_id}";
} elseif ( $post_id > 0 ) {
$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 );
$cache_key = "sharing_service_get_total_b{$_blog_id}_p{$post_id}_s{$service_name}";
$sql = $wpdb->prepare( 'SELECT SUM( count ) FROM sharing_stats WHERE blog_id = %d AND share_service = %s', $_blog_id, $service_name );
$cache_key = "sharing_service_get_total_b{$_blog_id}_s{$service_name}";
$ret = wp_cache_get( $cache_key, 'sharing' );
// phpcs:ignore WordPress.DB.PreparedSQL.NotPrepared, WordPress.DB.DirectDatabaseQuery.DirectQuery -- Prepared above.
$ret = (int) $wpdb->get_var( $sql );
wp_cache_set( $cache_key, $ret, 'sharing', 5 * MINUTE_IN_SECONDS );
* Get total stats for a site, for all sharing services.
* @param int|bool $post_id Post ID.
public function get_services_total( $post_id = false ) {
$services = $this->get_blog_services();
if ( ! empty( $services ) && isset( $services['all'] ) ) {
foreach ( $services['all'] as $key => $value ) {
$totals[ $key ] = new Sharing_Service_Total( $key, $this->get_total( $key, $post_id ) );
usort( $totals, array( 'Sharing_Service_Total', 'cmp' ) );
* Get sharing stats for all posts on the site.
public function get_posts_total() {
$cache_key = "sharing_service_get_posts_total_{$blog_id}";
$my_data = wp_cache_get( $cache_key, 'sharing' );
if ( $my_data === false ) {
$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
wp_cache_set( $cache_key, $my_data, 'sharing', 5 * MINUTE_IN_SECONDS );
if ( ! empty( $my_data ) ) {
foreach ( $my_data as $row ) {
$totals[] = new Sharing_Post_Total( $row->id, $row->total );
usort( $totals, array( 'Sharing_Post_Total', 'cmp' ) );
* Get stats for a specific sharing service.
class Sharing_Service_Total {
* Total number of shares for this service.
* @param int $id Service ID.
* @param int $total Total shares.
public function __construct( $id, $total ) {
$services = new Sharing_Service();
$this->id = esc_html( $id );
$this->service = $services->get_service( $id );
$this->total = (int) $total;
if ( $this->service instanceof Sharing_Source ) {
$this->name = $this->service->get_name();
* Compare total shares between 2 posts.
* @param object $a Sharing_Service_Total object.
* @param object $b Sharing_Service_Total object.
* @return int -1, 0, or 1 if $a is <, =, or > $b
public static function cmp( $a, $b ) {
if ( $a->total === $b->total ) {
return $b->name <=> $a->name;
return $b->total <=> $a->total;
* Get sharing stats for a specific post.
class Sharing_Post_Total {
* @param int $id Service ID.
* @param int $total Total shares.
public function __construct( $id, $total ) {
$this->total = (int) $total;
$this->title = get_the_title( $this->id );
$this->url = get_permalink( $this->id );
* Compare total shares between 2 posts.
* @param object $a Sharing_Post_Total object.
* @param object $b Sharing_Post_Total object.
* @return int -1, 0, or 1 if $a is <, =, or > $b
public static function cmp( $a, $b ) {
if ( $a->total === $b->total ) {
return $b->id <=> $a->id;
return $b->total <=> $a->total;
* Populate sharing counts global with a post we want to count shares for.
* @param int $post_id Post ID.
function sharing_register_post_for_share_counts( $post_id ) {
global $jetpack_sharing_counts;
if ( ! isset( $jetpack_sharing_counts ) || ! is_array( $jetpack_sharing_counts ) ) {
$jetpack_sharing_counts = array();
$jetpack_sharing_counts[ (int) $post_id ] = get_permalink( $post_id );
* Determine whether we should load sharing scripts or not.
function sharing_maybe_enqueue_scripts() {
$sharer = new Sharing_Service();
$global_options = $sharer->get_global_options();
if ( is_singular() && in_array( get_post_type(), $global_options['show'], true ) ) {
in_array( 'index', $global_options['show'], true )
|| in_array( get_post_type(), $global_options['show'], true )
* Filter to decide when sharing scripts should be enqueued.
* @param bool $enqueue Decide if the sharing scripts should be enqueued.
return (bool) apply_filters( 'sharing_enqueue_scripts', $enqueue );
* Add sharing JavaScript to the footer of a page.
function sharing_add_footer() {
class_exists( 'Jetpack_AMP_Support' )
&& Jetpack_AMP_Support::is_amp_request()
global $jetpack_sharing_counts;
* Filter all JavaScript output by the sharing module.
* @param bool true Control whether the sharing module should add any JavaScript to the site. Default to true.
apply_filters( 'sharing_js', true )
&& sharing_maybe_enqueue_scripts()
* Filter the display of sharing counts next to the sharing buttons.
* @param bool true Control the display of counters next to the sharing buttons. Default to true.
apply_filters( 'jetpack_sharing_counts', true )
&& is_array( $jetpack_sharing_counts )
&& count( $jetpack_sharing_counts )
$sharing_post_urls = array_filter( $jetpack_sharing_counts );
if ( $sharing_post_urls ) :
<script type="text/javascript">
window.WPCOM_sharing_counts = <?php echo wp_json_encode( array_flip( $sharing_post_urls ), JSON_UNESCAPED_SLASHES | JSON_HEX_TAG | JSON_HEX_AMP ); ?>;
wp_enqueue_script( 'sharing-js' );
$sharing_js_options = array(
'lang' => get_base_recaptcha_lang_code(),
/** This filter is documented in modules/sharedaddy/sharing-service.php */
'counts' => apply_filters( 'jetpack_sharing_counts', true ),
'is_stats_active' => Jetpack::is_module_active( 'stats' ),
wp_localize_script( 'sharing-js', 'sharing_js_options', $sharing_js_options );
$sharer = new Sharing_Service();
$enabled = $sharer->get_blog_services();
foreach ( array_merge( $enabled['visible'], $enabled['hidden'] ) as $service ) {
$service->display_footer();
* Enqueue sharing CSS in head.
function sharing_add_header() {
$sharer = new Sharing_Service();
$enabled = $sharer->get_blog_services();
foreach ( array_merge( $enabled['visible'], $enabled['hidden'] ) as $service ) {
$service->display_header();
if ( is_countable( $enabled['all'] ) && ( count( $enabled['all'] ) > 0 ) && sharing_maybe_enqueue_scripts() ) {
wp_enqueue_style( 'sharedaddy', plugin_dir_url( __FILE__ ) . 'sharing.css', array(), JETPACK__VERSION );
wp_enqueue_style( 'social-logos' );
add_action( 'wp_head', 'sharing_add_header', 1 );
* Launch sharing requests on page load when a specific query string is used.
function sharing_process_requests() {
// Only process if: single post and share=X defined
if ( ( is_page() || is_single() ) && isset( $_GET['share'] ) && is_string( $_GET['share'] ) ) { // phpcs:ignore WordPress.Security.NonceVerification.Recommended
$sharer = new Sharing_Service();
$service = $sharer->get_service( sanitize_text_field( wp_unslash( $_GET['share'] ) ) ); // phpcs:ignore WordPress.Security.NonceVerification.Recommended
$service->process_request( $post, $_POST ); // phpcs:ignore WordPress.Security.NonceVerification.Missing
// phpcs:ignore WordPress.Security.NonceVerification.Recommended -- Only checking for the data being present.
if ( isset( $_GET['share'] ) ) {
add_action( 'template_redirect', 'sharing_process_requests', 9 );
* Gets the url to customise the sharing buttons in WP-Admin.
* @return string the customisation URL.
function get_sharing_buttons_customisation_url() {
return admin_url( 'options-general.php?page=sharing' );
* Append sharing links to text.
* @param string $text The original text to append sharing links onto.
* @param bool $echo Where to echo the text or return.
* @return string The original $text with, if conditions are met, the sharing links.
function sharing_display( $text = '', $echo = false ) {
global $post, $wp_current_filter;
if ( Settings::is_syncing() ) {
// We require the post to not be empty and be an actual WordPress post object. If it's not - we just return.
if ( empty( $post ) || ! $post instanceof \WP_Post ) {
if ( ( is_preview() || is_admin() ) && ! ( defined( 'DOING_AJAX' ) && DOING_AJAX ) ) {
// Prevent from rendering sharing buttons in block which is fetched from REST endpoint by editor
if ( defined( 'REST_REQUEST' ) && REST_REQUEST ) {
// Do not output sharing buttons for ActivityPub requests.
function_exists( '\Activitypub\is_activitypub_request' )
&& \Activitypub\is_activitypub_request()
// Don't output flair on excerpts.
if ( in_array( 'get_the_excerpt', (array) $wp_current_filter, true ) ) {
// Ensure we don't display sharing buttons on post excerpts that are hooked inside the post content
if ( in_array( 'the_excerpt', (array) $wp_current_filter, true ) &&
in_array( 'the_content', (array) $wp_current_filter, true ) ) {
// Don't allow flair to be added to the_content more than once (prevent infinite loops).