Plugin Directory

Changeset 3023121


Ignore:
Timestamp:
01/17/2024 07:21:25 PM (2 years ago)
Author:
billerickson
Message:

tagging 1.5.0

Location:
shared-counts
Files:
16 edited
1 copied

Legend:

Unmodified
Added
Removed
  • shared-counts/tags/1.5.0/CHANGELOG.md

    r2836327 r3023121  
    11# Change Log
    22All notable changes to this project will be documented in this file, formatted via [this recommendation](http://keepachangelog.com/).
     3
     4## [1.5.0] = 2024-01-17
     5### Changed
     6- Replaced server side email with a mailto link to remove the possibility of spam, see #131
     7- Updated the Twitter logo to X, see #129
     8- Removed jQuery dependency, see #113
     9- Use `esc_url()` for sharing links, see #130
    310
    411## [1.4.1] = 2022-12-19
  • shared-counts/tags/1.5.0/assets/js/shared-counts.js

    r2087195 r3023121  
    1 /* global shared_counts, grecaptcha */
    2 
    3 'use strict';
    4 
    5 jQuery( document ).ready(function($){
    6 
    7     var modalOpen = false,
    8         shared_counts_id,
    9         shared_counts_nonce;
    10 
    11     /**
    12      * Close email sharing modal.
    13      *
    14      * @since 1.0.0
    15      */
    16     function modalClose() {
    17 
    18         modalOpen = false;
    19 
    20         $( '#shared-counts-modal-recipient, #shared-counts-modal-name, #shared-counts-modal-email' ).val( '' );
    21         $( '#shared-counts-modal-wrap' ).fadeOut();
    22         $( '#shared-counts-modal-sent' ).hide();
    23     }
     1document.addEventListener('DOMContentLoaded', function() {
    242
    253    // Google Analytics Social tracking.
    26     $( document ).on( 'click', '.shared-counts-button', function() {
     4    document.addEventListener('click', function(event) {
     5        var target = event.target;
    276
    28         if ( shared_counts.social_tracking ) {
     7        if (target.classList.contains('shared-counts-button')) {
    298
    30             var $this   = $( this ),
    31                 network = $this.data( 'social-network' ),
    32                 action  = $this.data( 'social-action' ),
    33                 target  = $this.data( 'social-target' );
     9            if (shared_counts.social_tracking) {
    3410
    35             if ( network && action && target ) {
    36                 if ( 'function' === typeof ga ) {
    37                     // Default GA.
    38                     ga( 'send', 'social', network, action, target );
    39                 } else if ( 'function' === typeof __gaTracker ) {
    40                     // MonsterInsights.
    41                     __gaTracker( 'send', 'social', network, action, target );
     11                var network = target.getAttribute('data-social-network'),
     12                    action = target.getAttribute('data-social-action'),
     13                    targetValue = target.getAttribute('data-social-target');
     14
     15                if (network && action && targetValue) {
     16                    if (typeof ga === 'function') {
     17                        // Default GA.
     18                        ga('send', 'social', network, action, targetValue);
     19                    } else if (typeof __gaTracker === 'function') {
     20                        // MonsterInsights.
     21                        __gaTracker('send', 'social', network, action, targetValue);
     22                    }
    4223                }
    4324            }
     
    4627
    4728    // Share button click.
    48     $( document ).on( 'click', '.shared-counts-button[target="_blank"]:not(.no-js)', function( event ) {
     29    document.addEventListener('click', function(event) {
     30        var target = event.target;
    4931
    50         event.preventDefault();
     32        if (target.classList.contains('shared-counts-button') && target.getAttribute('target') === '_blank' && !target.classList.contains('no-js')) {
    5133
    52         var window_size = '',
    53             url         = this.href,
    54             domain      = url.split( '/' )[2];
     34            event.preventDefault();
    5535
    56         switch ( domain ) {
    57             case 'www.facebook.com':
    58                 window_size = 'width=585,height=368';
    59                 break;
    60             case 'twitter.com':
    61                 window_size = 'width=585,height=261';
    62                 break;
    63             case 'pinterest.com':
    64                 window_size = 'width=750,height=550';
    65                 break;
    66             default:
    67                 window_size = 'width=585,height=515';
    68         }
    69         window.open( url, '', 'menubar=no,toolbar=no,resizable=yes,scrollbars=yes,' + window_size );
     36            var window_size = '',
     37                url = target.href,
     38                domain = url.split('/')[2];
    7039
    71         $( this ).trigger( 'shared-counts-click' );
    72     });
     40            switch (domain) {
     41                case 'www.facebook.com':
     42                    window_size = 'width=585,height=368';
     43                    break;
     44                case 'twitter.com':
     45                    window_size = 'width=585,height=261';
     46                    break;
     47                case 'pinterest.com':
     48                    window_size = 'width=750,height=550';
     49                    break;
     50                default:
     51                    window_size = 'width=585,height=515';
     52            }
     53            window.open(url, '', 'menubar=no,toolbar=no,resizable=yes,scrollbars=yes,' + window_size);
    7354
    74     // Email share button, opens email modal.
    75     $( document ).on( 'click', 'a[href*="#shared-counts-email"]', function( event ) {
    76 
    77         event.preventDefault();
    78 
    79         modalOpen = true;
    80 
    81         // Show modal and focus on first field.
    82         $( '#shared-counts-modal-wrap' ).fadeIn();
    83         $( '#shared-counts-modal-recipient' ).focus();
    84 
    85         // Set data needed to send.
    86         shared_counts_id    = $( this ).data( 'postid' );
    87         shared_counts_nonce = $( this ).data( 'nonce' );
    88 
    89         // Maybe load reCAPTCHA.
    90         if ( shared_counts.recaptchaSitekey ) {
    91             grecaptcha.render( 'shared-counts-modal-recaptcha', {
    92                 sitekey:  shared_counts.recaptchaSitekey
    93             } );
     55            target.dispatchEvent(new Event('shared-counts-click'));
    9456        }
    9557    });
    9658
    97     // Close email modal by overlay or close button click.
    98     $( document ).on( 'click', '#shared-counts-modal-close, #shared-counts-modal-wrap', function( event ) {
    99 
    100         // If the modal wrap was clicked, verify it was the actual element, and
    101         // not something inside it.
    102         if ( 'shared-counts-modal-wrap' === $( this ).attr( 'id' ) && ! $( event.target ).is( '#shared-counts-modal-wrap' ) ) {
    103             return;
    104         }
    105 
    106         event.preventDefault();
    107 
    108         modalClose();
    109     });
    110 
    111     // Close email modal if Esc key is pressed while it is open.
    112     $( document ).keyup( function( event ) {
    113         if ( modalOpen && event.keyCode === 27 ) {
    114             modalClose();
    115         }
    116     });
    117 
    118     // Submit email share via email modal.
    119     $( document ).on( 'click', '#shared-counts-modal-submit', function( event ) {
    120 
    121         event.preventDefault();
    122 
    123         var empty       = false,
    124             $this       = $( this ),
    125             $recipient  = $( '#shared-counts-modal-recipient' ),
    126             $name       = $( '#shared-counts-modal-name' ),
    127             $email      = $( '#shared-counts-modal-email' ),
    128             $validation = $( '#shared-counts-modal-validation' ),
    129             $recaptcha  = $( '#g-recaptcha-response' ),
    130             data        = {
    131                 action:    'shared_counts_email',
    132                 postid:     shared_counts_id,
    133                 recipient:  $recipient.val(),
    134                 name:       $name.val(),
    135                 email:      $email.val(),
    136                 validation: $validation.val(),
    137                 recaptcha:  $recaptcha.val(),
    138                 nonce:      shared_counts_nonce
    139             };
    140 
    141         // Check if any of the required fields are empty.
    142         $( $recipient, $name, $email ).each(function() {
    143             if ( ! $( this ).val() || $( this ).val() === '' ) {
    144                 empty = true;
    145             }
    146         });
    147 
    148         // If an empty field was found, alert user and stop.
    149         if ( empty ) {
    150             alert( shared_counts.email_fields_required );
    151             return;
    152         }
    153 
    154         // Disable submit to prevent duplicates.
    155         $( this ).prop( 'disabled', true );
    156 
    157         // AJAX post.
    158         $.post( shared_counts.ajaxurl, data, function( res ) {
    159 
    160             if ( res.success ){
    161                 console.log( shared_counts.email_sent );
    162 
    163                 modalClose();
    164             }
    165 
    166             // Enable submit button.
    167             $this.prop( 'disabled', false );
    168 
    169         }).fail( function( xhr ) {
    170             console.log( xhr.responseText );
    171         });
    172     });
    17359});
  • shared-counts/tags/1.5.0/assets/js/shared-counts.min.js

    r2087195 r3023121  
    1 "use strict";jQuery(document).ready(function(d){var l,u,e=!1;function h(){e=!1,d("#shared-counts-modal-recipient, #shared-counts-modal-name, #shared-counts-modal-email").val(""),d("#shared-counts-modal-wrap").fadeOut(),d("#shared-counts-modal-sent").hide()}d(document).on("click",".shared-counts-button",function(){if(shared_counts.social_tracking){var a=d(this),e=a.data("social-network"),t=a.data("social-action"),o=a.data("social-target");e&&t&&o&&("function"==typeof ga?ga("send","social",e,t,o):"function"==typeof __gaTracker&&__gaTracker("send","social",e,t,o))}}),d(document).on("click",'.shared-counts-button[target="_blank"]:not(.no-js)',function(a){a.preventDefault();var e="",t=this.href;switch(t.split("/")[2]){case"www.facebook.com":e="width=585,height=368";break;case"twitter.com":e="width=585,height=261";break;case"pinterest.com":e="width=750,height=550";break;default:e="width=585,height=515"}window.open(t,"","menubar=no,toolbar=no,resizable=yes,scrollbars=yes,"+e),d(this).trigger("shared-counts-click")}),d(document).on("click",'a[href*="#shared-counts-email"]',function(a){a.preventDefault(),e=!0,d("#shared-counts-modal-wrap").fadeIn(),d("#shared-counts-modal-recipient").focus(),l=d(this).data("postid"),u=d(this).data("nonce"),shared_counts.recaptchaSitekey&&grecaptcha.render("shared-counts-modal-recaptcha",{sitekey:shared_counts.recaptchaSitekey})}),d(document).on("click","#shared-counts-modal-close, #shared-counts-modal-wrap",function(a){("shared-counts-modal-wrap"!==d(this).attr("id")||d(a.target).is("#shared-counts-modal-wrap"))&&(a.preventDefault(),h())}),d(document).keyup(function(a){e&&27===a.keyCode&&h()}),d(document).on("click","#shared-counts-modal-submit",function(a){a.preventDefault();var e=!1,t=d(this),o=d("#shared-counts-modal-recipient"),s=d("#shared-counts-modal-name"),n=d("#shared-counts-modal-email"),c=d("#shared-counts-modal-validation"),i=d("#g-recaptcha-response"),r={action:"shared_counts_email",postid:l,recipient:o.val(),name:s.val(),email:n.val(),validation:c.val(),recaptcha:i.val(),nonce:u};d(o,s,n).each(function(){d(this).val()&&""!==d(this).val()||(e=!0)}),e?alert(shared_counts.email_fields_required):(d(this).prop("disabled",!0),d.post(shared_counts.ajaxurl,r,function(a){a.success&&(console.log(shared_counts.email_sent),h()),t.prop("disabled",!1)}).fail(function(a){console.log(a.responseText)}))})});
     1document.addEventListener("DOMContentLoaded",function(){document.addEventListener("click",function(t){var e=t.target;if(e.classList.contains("shared-counts-button")&&shared_counts.social_tracking){var a=e.getAttribute("data-social-network"),i=e.getAttribute("data-social-action"),n=e.getAttribute("data-social-target");a&&i&&n&&("function"==typeof ga?ga("send","social",a,i,n):"function"==typeof __gaTracker&&__gaTracker("send","social",a,i,n))}}),document.addEventListener("click",function(t){var e=t.target;if(e.classList.contains("shared-counts-button")&&"_blank"===e.getAttribute("target")&&!e.classList.contains("no-js")){t.preventDefault();var a="",i=e.href,n=i.split("/")[2];switch(n){case"www.facebook.com":a="width=585,height=368";break;case"twitter.com":a="width=585,height=261";break;case"pinterest.com":a="width=750,height=550";break;default:a="width=585,height=515"}window.open(i,"","menubar=no,toolbar=no,resizable=yes,scrollbars=yes,"+a),e.dispatchEvent(new Event("shared-counts-click"))}})});
  • shared-counts/tags/1.5.0/includes/class-shared-counts-admin.php

    r2836328 r3023121  
    366366                                );
    367367                                ?>
    368                             </p>
    369                         </td>
    370                     </tr>
    371 
    372                     <!-- Enable Email reCAPTCHA (if email button is configured) -->
    373                     <tr valign="top" id="shared-counts-setting-row-recaptcha">
    374                         <th scope="row"><label for="shared-counts-setting-recaptcha"><?php esc_html_e( 'Enable Email reCAPTCHA', 'shared-counts' ); ?></label></th>
    375                         <td>
    376                             <input type="checkbox" name="shared_counts_options[recaptcha]" value="1" id="shared-counts-setting-recaptcha" <?php checked( $this->settings_value( 'recaptcha' ), 1 ); ?>>
    377                             <p class="description">
    378                                 <?php esc_html_e( 'Highly recommended, Google\'s v2 reCAPTCHA will protect the email sharing feature from abuse.', 'shared-counts' ); ?>
    379                             </p>
    380                         </td>
    381                     </tr>
    382 
    383                     <!-- Google reCAPTCHA Site key (if recaptcha is enabled) -->
    384                     <tr valign="top" id="shared-counts-setting-row-recaptcha_site_key">
    385                         <th scope="row"><label for="shared-counts-setting-recaptcha_site_key"><?php esc_html_e( 'reCAPTCHA Site Key', 'shared-counts' ); ?></label></th>
    386                         <td>
    387                             <input type="text" name="shared_counts_options[recaptcha_site_key]" value="<?php echo esc_attr( $this->settings_value( 'recaptcha_site_key' ) ); ?>" id="shared-counts-setting-recaptcha_site_key" class="regular-text" />
    388                             <p class="description">
    389                                 <?php esc_html_e( 'After signing up for Google\'s v2 reCAPTCHA (free), provide your site key here.', 'shared-counts' ); ?><br><a href="https://www.google.com/recaptcha/" target="_blank" rel="noopener noreferrer"><?php esc_html_e( 'Learn more.', 'shared-counts' ); ?></a>
    390                             </p>
    391                         </td>
    392                     </tr>
    393 
    394                     <!-- Google reCAPTCHA Secret key (if recaptcha is enabled) -->
    395                     <tr valign="top" id="shared-counts-setting-row-recaptcha_secret_key">
    396                         <th scope="row"><label for="shared-counts-setting-recaptcha_secret_key"><?php esc_html_e( 'reCAPTCHA Secret Key', 'shared-counts' ); ?></label></th>
    397                         <td>
    398                             <input type="text" name="shared_counts_options[recaptcha_secret_key]" value="<?php echo esc_attr( $this->settings_value( 'recaptcha_secret_key' ) ); ?>" id="shared-counts-setting-recaptcha_secret_key" class="regular-text" />
    399                             <p class="description">
    400                                 <?php esc_html_e( 'After signing up for Google\'s v2 reCAPTCHA (free), provide your secret key here.', 'shared-counts' ); ?>
    401368                            </p>
    402369                        </td>
     
    605572            'included_services'    => [ 'facebook', 'twitter', 'pinterest' ],
    606573            'query_services'       => [],
    607             'recaptcha'            => '',
    608             'recpatcha_site_key'   => '',
    609             'recaptcha_secret_key' => '',
    610574        ];
    611575    }
     
    692656        $input['theme_location']       = sanitize_text_field( $input['theme_location'] );
    693657        $input['included_services']    = ! empty( $input['included_services'] ) ? array_map( 'sanitize_text_field', $input['included_services'] ) : [];
    694         $input['recaptcha']            = ! empty( $input['recaptcha'] ) ? '1' : '';
    695         $input['recaptcha_site_key']   = sanitize_text_field( $input['recaptcha_site_key'] );
    696         $input['recaptcha_secret_key'] = sanitize_text_field( $input['recaptcha_secret_key'] );
    697 
    698658        return $input;
    699659    }
  • shared-counts/tags/1.5.0/includes/class-shared-counts-core.php

    r2836327 r3023121  
    4242     */
    4343    public function __construct() {
    44 
    45         add_action( 'wp_ajax_shared_counts_email', [ $this, 'email_ajax' ] );
    46         add_action( 'wp_ajax_nopriv_shared_counts_email', [ $this, 'email_ajax' ] );
    4744        add_action( 'shutdown', [ $this, 'shutdown_update_share_counts' ] );
    48     }
    49 
    50     /**
    51      * Process and send email share AJAX requests.
    52      *
    53      * @since 1.0.0
    54      */
    55     public function email_ajax() {
    56 
    57         $data = $_POST; // phpcs:ignore
    58 
    59         // Check spam honeypot.
    60         if ( ! empty( $data['validation'] ) ) {
    61             wp_send_json_error( __( 'Honeypot triggered.', 'shared-counts' ) );
    62         }
    63 
    64         // Check required fields.
    65         if ( empty( $data['recipient'] ) || empty( $data['name'] ) || empty( $data['email'] ) ) {
    66             wp_send_json_error( __( 'Required field missing.', 'shared-counts' ) );
    67         }
    68 
    69         // Check email addresses.
    70         if ( ! is_email( $data['recipient'] ) || ! is_email( $data['email'] ) ) {
    71             wp_send_json_error( __( 'Invalid email.', 'shared-counts' ) );
    72         }
    73 
    74         $options = shared_counts()->admin->options();
    75 
    76         // Confirm email sharing is enabled.
    77         if ( ! in_array( 'email', $options['included_services'], true ) ) {
    78             wp_send_json_error( __( 'Email not enabled.', 'shared-counts' ) );
    79         }
    80 
    81         // Check if reCAPTCHA is enabled.
    82         $recaptcha = ! empty( $options['recaptcha'] ) && ! empty( $options['recaptcha_site_key'] ) && ! empty( $options['recaptcha_secret_key'] );
    83 
    84         // reCAPTCHA is enabled, so verify it.
    85         if ( $recaptcha ) {
    86 
    87             if ( empty( $data['recaptcha'] ) ) {
    88                 wp_send_json_error( __( 'reCAPTCHA is required.', 'shared-counts' ) );
    89             }
    90 
    91             $api_results = wp_remote_get( 'https://www.google.com/recaptcha/api/siteverify?secret=' . $options['recaptcha_secret_key'] . '&response=' . $data['recaptcha'] );
    92             $results     = json_decode( wp_remote_retrieve_body( $api_results ) );
    93             if ( empty( $results->success ) ) {
    94                 wp_send_json_error( __( 'Incorrect reCAPTCHA, please try again.', 'shared-counts' ) );
    95             }
    96         }
    97 
    98         $post_id    = absint( $data['postid'] );
    99         $recipient  = sanitize_text_field( $data['recipient'] );
    100         $from_email = sanitize_text_field( $data['email'] );
    101         $from_name  = sanitize_text_field( $data['name'] );
    102         $site_name  = html_entity_decode( wp_strip_all_tags( get_bloginfo( 'name' ) ), ENT_QUOTES );
    103         $site_root  = strtolower( $_SERVER['SERVER_NAME'] ); //phpcs:ignore
    104         if ( substr( $site_root, 0, 4 ) === 'www.' ) {
    105             $site_root = substr( $site_root, 4 );
    106         }
    107 
    108         $headers = [
    109             sprintf( 'From: %s <noreply@%s>', $site_name, $site_root ),
    110             sprintf( 'Reply-To: %s <%s>', $from_name, $from_email ),
    111         ];
    112         /* translators: %1$s - Name of the person who shared the article. */
    113         $subject = sprintf( esc_html__( 'Your friend %1$s has shared an article with you.', 'shared-counts' ), $from_name );
    114         $body    = html_entity_decode( get_the_title( $post_id ), ENT_QUOTES ) . "\r\n";
    115         $body   .= get_permalink( $post_id ) . "\r\n";
    116 
    117         wp_mail(
    118             $recipient,
    119             apply_filters( 'shared_counts_email_subject', $subject, $post_id, $recipient, $from_name, $from_email ),
    120             apply_filters( 'shared_counts_email_body',    $body,    $post_id, $recipient, $from_name, $from_email ),
    121             apply_filters( 'shared_counts_email_headers', $headers, $post_id, $recipient, $from_name, $from_email )
    122         );
    123 
    124         // Don't track email shares if plugin is configured to omit counts.
    125         if ( ! empty( $options['count_source'] ) && 'none' !== $options['count_source'] ) {
    126             $count  = absint( get_post_meta( $post_id, 'shared_counts_email', true ) );
    127             $update = update_post_meta( $post_id, 'shared_counts_email', ++$count );
    128         }
    129 
    130         wp_send_json_success();
    13145    }
    13246
  • shared-counts/tags/1.5.0/includes/class-shared-counts-front.php

    r2727404 r3023121  
    4242        add_action( 'wp_enqueue_scripts', [ $this, 'header_assets' ], 9 );
    4343        add_action( 'wp_footer', [ $this, 'load_assets' ], 1 );
    44         add_action( 'wp_footer', [ $this, 'email_modal' ], 50 );
    4544        add_shortcode( 'shared_counts', [ $this, 'shortcode' ] );
    4645        add_action( 'admin_bar_menu', [ $this, 'admin_bar' ], 999 );
     
    150149            'shared-counts',
    151150            SHARED_COUNTS_URL . 'assets/js/shared-counts' . $suffix . '.js',
    152             [ 'jquery' ],
     151            [],
    153152            SHARED_COUNTS_VERSION,
    154153            true
     
    180179        }
    181180
    182         $options   = shared_counts()->admin->options();
    183         $recaptcha = ! empty( $options['recaptcha'] ) && ! empty( $options['recaptcha_site_key'] ) && ! empty( $options['recaptcha_secret_key'] );
    184 
    185181        // Load CSS.
    186182        if ( apply_filters( 'shared_counts_load_css', true ) ) {
     
    193189            wp_enqueue_script( 'shared-counts' );
    194190
    195             if ( $recaptcha ) {
    196                 wp_enqueue_script(
    197                     'recaptcha',
    198                     'https://www.google.com/recaptcha/api.js',
    199                     [],
    200                     '2.0',
    201                     true
    202                 );
    203             }
    204191        }
    205192
    206193        // Localize JS strings.
    207194        $args = [
    208             'email_fields_required' => esc_html__( 'Please complete out all 3 fields to email this article.', 'shared-counts' ),
    209             'email_sent'            => esc_html__( 'Article successfully shared.', 'shared-counts' ),
    210             'ajaxurl'               => esc_url_raw( admin_url( 'admin-ajax.php' ) ),
    211195            'social_tracking'       => apply_filters( 'shared_counts_social_tracking', true ),
    212196        ];
    213197
    214         // Localize recaptcha site key if enabled.
    215         if ( $recaptcha ) {
    216             $args['recaptchaSitekey'] = sanitize_text_field( $options['recaptcha_site_key'] );
    217         }
    218 
    219198        wp_localize_script( 'shared-counts', 'shared_counts', $args );
    220     }
    221 
    222     /**
    223      * Email modal pop-up.
    224      *
    225      * This popup is output (and hidden) in the site footer if the Email
    226      * service is configured in the plugin settings.
    227      *
    228      * @since 1.0.0
    229      */
    230     public function email_modal() {
    231 
    232         // Only continue if a share link is on the page.
    233         if ( ! $this->share_link ) {
    234             return;
    235         }
    236 
    237         // Filter to disable email modal
    238         if( apply_filters( 'shared_counts_disable_email_modal', false ) ) {
    239             return;
    240         }
    241 
    242         // Check to see the email button is configured or being overriden. The
    243         // filter can be used to enable the modal in use cases where the share
    244         // button is manually being called.
    245         $options = shared_counts()->admin->options();
    246 
    247         if ( ! in_array( 'email', $options['included_services'], true ) && ! apply_filters( 'shared_counts_email_modal', false ) ) {
    248             return;
    249         }
    250 
    251         // Check for reCAPTCHA settings.
    252         $recaptcha = ! empty( $options['recaptcha'] ) && ! empty( $options['recaptcha_site_key'] ) && ! empty( $options['recaptcha_secret_key'] );
    253 
    254         // Labels, filterable of course.
    255         $labels = apply_filters(
    256             'shared_counts_email_labels',
    257             [
    258                 'title'      => esc_html__( 'Share this Article', 'shared-counts' ),
    259                 'subtitle'   => esc_html__( 'Like this article? Email it to a friend!', 'shared-counts' ),
    260                 'title_icon' => '<svg xmlns="http://www.w3.org/2000/svg" width="48" height="45" viewBox="0 0 48 45"><path fill-rule="evenodd" d="M31.7849302,27.028093 L27.9750698,27.028093 L27.9750698,19.6751628 C27.9750698,18.7821395 27.9940465,17.9650233 28.0331163,17.2249302 C27.7986977,17.5095814 27.5062326,17.8065116 27.1579535,18.1179535 L25.5806512,19.4195349 L23.6327442,17.024 L28.4026047,13.1393488 L31.7849302,13.1393488 L31.7849302,27.028093 Z M22.6392558,21.4422326 L19.104,21.4422326 L19.104,24.8714419 L16.5488372,24.8714419 L16.5488372,21.4422326 L13.015814,21.4422326 L13.015814,18.896 L16.5488372,18.896 L16.5488372,15.4098605 L19.104,15.4098605 L19.104,18.896 L22.6392558,18.896 L22.6392558,21.4422326 Z M43.5996279,2 L4.40037209,2 C1.9735814,2 0,3.97469767 0,6.40037209 L0,32.8003721 C0,35.2260465 1.9735814,37.1996279 4.40037209,37.1996279 L22.3791628,37.1996279 L33.2796279,45.92 C33.5843721,46.1633488 33.9505116,46.2883721 34.3211163,46.2883721 C34.5689302,46.2883721 34.8178605,46.2325581 35.0511628,46.119814 C35.636093,45.8385116 36,45.2591628 36,44.6106047 L36,37.1996279 L43.5996279,37.1996279 C46.0253023,37.1996279 48,35.2260465 48,32.8003721 L48,6.40037209 C48,3.97469767 46.0253023,2 43.5996279,2 Z" transform="translate(0 -2)"/></svg>',
    261                 'recipient'  => esc_html__( 'Friend\'s Email Address', 'shared-counts' ),
    262                 'name'       => esc_html__( 'Your Name', 'shared-counts' ),
    263                 'email'      => esc_html__( 'Your Email Address', 'shared-counts' ),
    264                 'validation' => esc_html__( 'Comments', 'shared-counts' ),
    265                 'close'      => '<span class="close-icon"><svg xmlns="http://www.w3.org/2000/svg" width="8" height="8" viewBox="0 0 8 8">
    266                 <path fill="#FFF" fill-rule="evenodd" d="M338,11.0149385 L340.805644,8.20929447 C341.000906,8.01403233 341.317489,8.01403233 341.512751,8.20929447 L341.790706,8.48724919 C341.985968,8.68251134 341.985968,8.99909383 341.790706,9.19435597 L338.985062,12 L341.790706,14.805644 C341.985968,15.0009062 341.985968,15.3174887 341.790706,15.5127508 L341.512751,15.7907055 C341.317489,15.9859677 341.000906,15.9859677 340.805644,15.7907055 L338,12.9850615 L335.194356,15.7907055 C334.999094,15.9859677 334.682511,15.9859677 334.487249,15.7907055 L334.209294,15.5127508 C334.014032,15.3174887 334.014032,15.0009062 334.209294,14.805644 L337.014938,12 L334.209294,9.19435597 C334.014032,8.99909383 334.014032,8.68251134 334.209294,8.48724919 L334.487249,8.20929447 C334.682511,8.01403233 334.999094,8.01403233 335.194356,8.20929447 L338,11.0149385 Z" transform="translate(-334 -8)"/>
    267                 </svg></span>',
    268                 'submit'     => esc_html__( 'Send Email', 'shared-counts' ),
    269             ]
    270         );
    271         ?>
    272         <div id="shared-counts-modal-wrap" style="display:none;">
    273             <div class="shared-counts-modal">
    274                 <a href="#" id="shared-counts-modal-close" aria-label="Close the share by email popup"><?php echo $labels['close']; // phpcs:ignore ?></a>
    275                 <div class="shared-counts-modal-header">
    276                     <?php
    277                     if ( ! empty( $labels['title_icon'] ) ) {
    278                         echo '<span class="shared-counts-modal-icon">' . $labels['title_icon'] . '</span>'; // phpcs:ignore
    279                     }
    280                     if ( ! empty( $labels['title'] ) ) {
    281                         echo '<span class="shared-counts-modal-title">' . esc_html( $labels['title'] ) . '</span>';
    282                     }
    283                     if ( ! empty( $labels['subtitle'] ) ) {
    284                         echo '<span class="shared-counts-modal-subtitle">' . esc_html( $labels['subtitle'] ) . '</span>';
    285                     }
    286                     ?>
    287                 </div>
    288                 <div class="shared-counts-modal-content">
    289                     <p>
    290                         <label for="shared-counts-modal-recipient"><?php echo esc_html( $labels['recipient'] ); ?></label>
    291                         <input type="email" id="shared-counts-modal-recipient" placeholder="<?php echo esc_html( $labels['recipient'] ); ?>">
    292                     </p>
    293                     <p>
    294                         <label for="shared-counts-modal-name"><?php echo esc_html( $labels['name'] ); ?></label>
    295                         <input type="text" id="shared-counts-modal-name" placeholder="<?php echo esc_html( $labels['name'] ); ?>">
    296                     </p>
    297                     <p>
    298                         <label for="shared-counts-modal-email"><?php echo esc_html( $labels['email'] ); ?></label>
    299                         <input type="email" id="shared-counts-modal-email" placeholder="<?php echo esc_html( $labels['email'] ); ?>">
    300                     </p>
    301                     <?php
    302                     if ( $recaptcha ) {
    303                         echo '<div id="shared-counts-modal-recaptcha"></div>';
    304                     }
    305                     ?>
    306                     <p class="shared-counts-modal-validation">
    307                         <label for="shared-counts-modal-validation"><?php echo esc_html( $labels['validation'] ); ?></label>
    308                         <input type="text" id="shared-counts-modal-validation" autocomplete="off">
    309                     </p>
    310                     <p class="shared-counts-modal-submit">
    311                         <button id="shared-counts-modal-submit"><?php echo $labels['submit']; // phpcs:ignore ?></button>
    312                     </p>
    313                     <div id="shared-counts-modal-sent"><?php esc_html_e( 'Email sent!', 'shared-counts' ); ?></div>
    314                 </div>
    315             </div>
    316         </div>
    317         <?php
    318199    }
    319200
     
    537418                    $link['link']           = 'https://twitter.com/share?url=' . $link['url'] . '&text=' . htmlspecialchars( rawurlencode( html_entity_decode( $link['title'], ENT_COMPAT, 'UTF-8' ) ), ENT_COMPAT, 'UTF-8' );
    538419                    $link['label']          = esc_html__( 'Tweet', 'shared-counts' );
    539                     $link['icon']           = '<svg version="1.1" xmlns="http://www.w3.org/2000/svg" width="29.71875" height="32" viewBox="0 0 951 1024"><path d="M925.714 233.143q-38.286 56-92.571 95.429 0.571 8 0.571 24 0 74.286-21.714 148.286t-66 142-105.429 120.286-147.429 83.429-184.571 31.143q-154.857 0-283.429-82.857 20 2.286 44.571 2.286 128.571 0 229.143-78.857-60-1.143-107.429-36.857t-65.143-91.143q18.857 2.857 34.857 2.857 24.571 0 48.571-6.286-64-13.143-106-63.714t-42-117.429v-2.286q38.857 21.714 83.429 23.429-37.714-25.143-60-65.714t-22.286-88q0-50.286 25.143-93.143 69.143 85.143 168.286 136.286t212.286 56.857q-4.571-21.714-4.571-42.286 0-76.571 54-130.571t130.571-54q80 0 134.857 58.286 62.286-12 117.143-44.571-21.143 65.714-81.143 101.714 53.143-5.714 106.286-28.571z"></path></svg>';
     420                    $link['icon']           = '<svg viewBox="0 0 24 24" xmlns="http://www.w3.org/2000/svg"><path d="M13.8944 10.4695L21.3345 2H19.5716L13.1085 9.35244L7.95022 2H1.99936L9.80147 13.1192L1.99936 22H3.76218L10.5832 14.2338L16.0318 22H21.9827L13.8944 10.4695ZM11.4792 13.2168L10.6875 12.1089L4.39789 3.30146H7.10594L12.1833 10.412L12.9717 11.5199L19.5708 20.7619H16.8628L11.4792 13.2168Z" /></svg>';
    540421                    $link['target']         = '_blank';
    541422                    $link['rel']            = 'nofollow noopener noreferrer';
     
    588469                    break;
    589470                case 'email':
    590                     $link['link']           = '#shared-counts-email';
     471                    $subject                = esc_html__( 'Your friend has shared an article with you.', 'shared-counts' );
     472                    $subject                = apply_filters( 'shared_counts_amp_email_subject', $subject, $id );
     473                    $body                   = html_entity_decode( get_the_title( $id ), ENT_QUOTES ) . "\r\n";
     474                    $body                  .= get_permalink( $id ) . "\r\n";
     475                    $body                   = apply_filters( 'shared_counts_amp_email_body', $body, $id );
     476                    $link['link']           = 'mailto:?subject=' . rawurlencode( $subject ) . '&body=' . rawurlencode( $body );
    591477                    $link['label']          = esc_html__( 'Email', 'shared-counts' );
    592478                    $link['icon']           = '<svg version="1.1" xmlns="http://www.w3.org/2000/svg" width="32" height="32" viewBox="0 0 1024 1024"><path d="M1024 405.714v453.714q0 37.714-26.857 64.571t-64.571 26.857h-841.143q-37.714 0-64.571-26.857t-26.857-64.571v-453.714q25.143 28 57.714 49.714 206.857 140.571 284 197.143 32.571 24 52.857 37.429t54 27.429 62.857 14h1.143q29.143 0 62.857-14t54-27.429 52.857-37.429q97.143-70.286 284.571-197.143 32.571-22.286 57.143-49.714zM1024 237.714q0 45.143-28 86.286t-69.714 70.286q-214.857 149.143-267.429 185.714-5.714 4-24.286 17.429t-30.857 21.714-29.714 18.571-32.857 15.429-28.571 5.143h-1.143q-13.143 0-28.571-5.143t-32.857-15.429-29.714-18.571-30.857-21.714-24.286-17.429q-52-36.571-149.714-104.286t-117.143-81.429q-35.429-24-66.857-66t-31.429-78q0-44.571 23.714-74.286t67.714-29.714h841.143q37.143 0 64.286 26.857t27.143 64.571z"></path></svg>';
     
    683569                $elements['wrap_open']  = sprintf(
    684570                    '<a href="%s"%s%s%s class="%s"%s>',
    685                     esc_attr( $link['link'] ),
     571                    esc_url( $link['link'] ),
    686572                    $attr_title,
    687573                    $target,
  • shared-counts/tags/1.5.0/readme.txt

    r2980287 r3023121  
    11=== Shared Counts - Social Media Share Buttons ===
    2 Contributors: jaredatch, billerickson, cdils
     2Contributors: jaredatch, billerickson
    33Tags: sharing, share buttons, social buttons, share counts, social, facebook, linkedin, pinterest, twitter
    44Requires at least: 4.6
    5 Tested up to: 6.1
    6 Stable tag: 1.4.1
     5Tested up to: 6.4
     6Stable tag: 1.5.0
    77Requires PHP: 5.6
    88License: GPLv2 or later
     
    2020- Yummly Button
    2121- Twitter Tweet Button (using the third-party Twitcount.com API)
    22 - Email Sharing (with reCAPTCHA support to prevent abuse)
     22- Email Sharing
    2323- Share Count Total
    2424- Print Button
     
    7575If you would like to include Twitter share counts, you can sign up for a free account at [twitcount.com](https://twitcount.com).
    7676
    77 If you use the Email share button, we recommend you enable Google's reCAPTCHA to prevent spam. [Sign up here](https://www.google.com/recaptcha/intro/android.html) (free) to get your Site Key and Secret Key.
    78 
    7977== Screenshots ==
    8078
     
    8280
    8381== Changelog ==
     82
     83**1.5.0**
     84- Changed: Replaced server side email with a mailto link to remove the possibility of spam, see #131
     85- Changed: Updated the Twitter logo to X, see #129
     86- Changed: Removed jQuery dependency, see #113
     87- Changed: Use `esc_url()` for sharing links, see #130
    8488
    8589**1.4.0**
  • shared-counts/tags/1.5.0/shared-counts.php

    r2836327 r3023121  
    55 * Description: Social sharing buttons that look great and keep your site loading fast.
    66 * Author:      Bill Erickson & Jared Atchison
    7  * Version:     1.4.1
     7 * Version:     1.5.0
    88 *
    99 * Shared Counts is free software: you can redistribute it and/or modify
     
    3636 */
    3737// Version.
    38 define( 'SHARED_COUNTS_VERSION', '1.4.1' );
     38define( 'SHARED_COUNTS_VERSION', '1.5.0' );
    3939
    4040// Directory path.
  • shared-counts/trunk/CHANGELOG.md

    r2836327 r3023121  
    11# Change Log
    22All notable changes to this project will be documented in this file, formatted via [this recommendation](http://keepachangelog.com/).
     3
     4## [1.5.0] = 2024-01-17
     5### Changed
     6- Replaced server side email with a mailto link to remove the possibility of spam, see #131
     7- Updated the Twitter logo to X, see #129
     8- Removed jQuery dependency, see #113
     9- Use `esc_url()` for sharing links, see #130
    310
    411## [1.4.1] = 2022-12-19
  • shared-counts/trunk/assets/js/shared-counts.js

    r2087195 r3023121  
    1 /* global shared_counts, grecaptcha */
    2 
    3 'use strict';
    4 
    5 jQuery( document ).ready(function($){
    6 
    7     var modalOpen = false,
    8         shared_counts_id,
    9         shared_counts_nonce;
    10 
    11     /**
    12      * Close email sharing modal.
    13      *
    14      * @since 1.0.0
    15      */
    16     function modalClose() {
    17 
    18         modalOpen = false;
    19 
    20         $( '#shared-counts-modal-recipient, #shared-counts-modal-name, #shared-counts-modal-email' ).val( '' );
    21         $( '#shared-counts-modal-wrap' ).fadeOut();
    22         $( '#shared-counts-modal-sent' ).hide();
    23     }
     1document.addEventListener('DOMContentLoaded', function() {
    242
    253    // Google Analytics Social tracking.
    26     $( document ).on( 'click', '.shared-counts-button', function() {
     4    document.addEventListener('click', function(event) {
     5        var target = event.target;
    276
    28         if ( shared_counts.social_tracking ) {
     7        if (target.classList.contains('shared-counts-button')) {
    298
    30             var $this   = $( this ),
    31                 network = $this.data( 'social-network' ),
    32                 action  = $this.data( 'social-action' ),
    33                 target  = $this.data( 'social-target' );
     9            if (shared_counts.social_tracking) {
    3410
    35             if ( network && action && target ) {
    36                 if ( 'function' === typeof ga ) {
    37                     // Default GA.
    38                     ga( 'send', 'social', network, action, target );
    39                 } else if ( 'function' === typeof __gaTracker ) {
    40                     // MonsterInsights.
    41                     __gaTracker( 'send', 'social', network, action, target );
     11                var network = target.getAttribute('data-social-network'),
     12                    action = target.getAttribute('data-social-action'),
     13                    targetValue = target.getAttribute('data-social-target');
     14
     15                if (network && action && targetValue) {
     16                    if (typeof ga === 'function') {
     17                        // Default GA.
     18                        ga('send', 'social', network, action, targetValue);
     19                    } else if (typeof __gaTracker === 'function') {
     20                        // MonsterInsights.
     21                        __gaTracker('send', 'social', network, action, targetValue);
     22                    }
    4223                }
    4324            }
     
    4627
    4728    // Share button click.
    48     $( document ).on( 'click', '.shared-counts-button[target="_blank"]:not(.no-js)', function( event ) {
     29    document.addEventListener('click', function(event) {
     30        var target = event.target;
    4931
    50         event.preventDefault();
     32        if (target.classList.contains('shared-counts-button') && target.getAttribute('target') === '_blank' && !target.classList.contains('no-js')) {
    5133
    52         var window_size = '',
    53             url         = this.href,
    54             domain      = url.split( '/' )[2];
     34            event.preventDefault();
    5535
    56         switch ( domain ) {
    57             case 'www.facebook.com':
    58                 window_size = 'width=585,height=368';
    59                 break;
    60             case 'twitter.com':
    61                 window_size = 'width=585,height=261';
    62                 break;
    63             case 'pinterest.com':
    64                 window_size = 'width=750,height=550';
    65                 break;
    66             default:
    67                 window_size = 'width=585,height=515';
    68         }
    69         window.open( url, '', 'menubar=no,toolbar=no,resizable=yes,scrollbars=yes,' + window_size );
     36            var window_size = '',
     37                url = target.href,
     38                domain = url.split('/')[2];
    7039
    71         $( this ).trigger( 'shared-counts-click' );
    72     });
     40            switch (domain) {
     41                case 'www.facebook.com':
     42                    window_size = 'width=585,height=368';
     43                    break;
     44                case 'twitter.com':
     45                    window_size = 'width=585,height=261';
     46                    break;
     47                case 'pinterest.com':
     48                    window_size = 'width=750,height=550';
     49                    break;
     50                default:
     51                    window_size = 'width=585,height=515';
     52            }
     53            window.open(url, '', 'menubar=no,toolbar=no,resizable=yes,scrollbars=yes,' + window_size);
    7354
    74     // Email share button, opens email modal.
    75     $( document ).on( 'click', 'a[href*="#shared-counts-email"]', function( event ) {
    76 
    77         event.preventDefault();
    78 
    79         modalOpen = true;
    80 
    81         // Show modal and focus on first field.
    82         $( '#shared-counts-modal-wrap' ).fadeIn();
    83         $( '#shared-counts-modal-recipient' ).focus();
    84 
    85         // Set data needed to send.
    86         shared_counts_id    = $( this ).data( 'postid' );
    87         shared_counts_nonce = $( this ).data( 'nonce' );
    88 
    89         // Maybe load reCAPTCHA.
    90         if ( shared_counts.recaptchaSitekey ) {
    91             grecaptcha.render( 'shared-counts-modal-recaptcha', {
    92                 sitekey:  shared_counts.recaptchaSitekey
    93             } );
     55            target.dispatchEvent(new Event('shared-counts-click'));
    9456        }
    9557    });
    9658
    97     // Close email modal by overlay or close button click.
    98     $( document ).on( 'click', '#shared-counts-modal-close, #shared-counts-modal-wrap', function( event ) {
    99 
    100         // If the modal wrap was clicked, verify it was the actual element, and
    101         // not something inside it.
    102         if ( 'shared-counts-modal-wrap' === $( this ).attr( 'id' ) && ! $( event.target ).is( '#shared-counts-modal-wrap' ) ) {
    103             return;
    104         }
    105 
    106         event.preventDefault();
    107 
    108         modalClose();
    109     });
    110 
    111     // Close email modal if Esc key is pressed while it is open.
    112     $( document ).keyup( function( event ) {
    113         if ( modalOpen && event.keyCode === 27 ) {
    114             modalClose();
    115         }
    116     });
    117 
    118     // Submit email share via email modal.
    119     $( document ).on( 'click', '#shared-counts-modal-submit', function( event ) {
    120 
    121         event.preventDefault();
    122 
    123         var empty       = false,
    124             $this       = $( this ),
    125             $recipient  = $( '#shared-counts-modal-recipient' ),
    126             $name       = $( '#shared-counts-modal-name' ),
    127             $email      = $( '#shared-counts-modal-email' ),
    128             $validation = $( '#shared-counts-modal-validation' ),
    129             $recaptcha  = $( '#g-recaptcha-response' ),
    130             data        = {
    131                 action:    'shared_counts_email',
    132                 postid:     shared_counts_id,
    133                 recipient:  $recipient.val(),
    134                 name:       $name.val(),
    135                 email:      $email.val(),
    136                 validation: $validation.val(),
    137                 recaptcha:  $recaptcha.val(),
    138                 nonce:      shared_counts_nonce
    139             };
    140 
    141         // Check if any of the required fields are empty.
    142         $( $recipient, $name, $email ).each(function() {
    143             if ( ! $( this ).val() || $( this ).val() === '' ) {
    144                 empty = true;
    145             }
    146         });
    147 
    148         // If an empty field was found, alert user and stop.
    149         if ( empty ) {
    150             alert( shared_counts.email_fields_required );
    151             return;
    152         }
    153 
    154         // Disable submit to prevent duplicates.
    155         $( this ).prop( 'disabled', true );
    156 
    157         // AJAX post.
    158         $.post( shared_counts.ajaxurl, data, function( res ) {
    159 
    160             if ( res.success ){
    161                 console.log( shared_counts.email_sent );
    162 
    163                 modalClose();
    164             }
    165 
    166             // Enable submit button.
    167             $this.prop( 'disabled', false );
    168 
    169         }).fail( function( xhr ) {
    170             console.log( xhr.responseText );
    171         });
    172     });
    17359});
  • shared-counts/trunk/assets/js/shared-counts.min.js

    r2087195 r3023121  
    1 "use strict";jQuery(document).ready(function(d){var l,u,e=!1;function h(){e=!1,d("#shared-counts-modal-recipient, #shared-counts-modal-name, #shared-counts-modal-email").val(""),d("#shared-counts-modal-wrap").fadeOut(),d("#shared-counts-modal-sent").hide()}d(document).on("click",".shared-counts-button",function(){if(shared_counts.social_tracking){var a=d(this),e=a.data("social-network"),t=a.data("social-action"),o=a.data("social-target");e&&t&&o&&("function"==typeof ga?ga("send","social",e,t,o):"function"==typeof __gaTracker&&__gaTracker("send","social",e,t,o))}}),d(document).on("click",'.shared-counts-button[target="_blank"]:not(.no-js)',function(a){a.preventDefault();var e="",t=this.href;switch(t.split("/")[2]){case"www.facebook.com":e="width=585,height=368";break;case"twitter.com":e="width=585,height=261";break;case"pinterest.com":e="width=750,height=550";break;default:e="width=585,height=515"}window.open(t,"","menubar=no,toolbar=no,resizable=yes,scrollbars=yes,"+e),d(this).trigger("shared-counts-click")}),d(document).on("click",'a[href*="#shared-counts-email"]',function(a){a.preventDefault(),e=!0,d("#shared-counts-modal-wrap").fadeIn(),d("#shared-counts-modal-recipient").focus(),l=d(this).data("postid"),u=d(this).data("nonce"),shared_counts.recaptchaSitekey&&grecaptcha.render("shared-counts-modal-recaptcha",{sitekey:shared_counts.recaptchaSitekey})}),d(document).on("click","#shared-counts-modal-close, #shared-counts-modal-wrap",function(a){("shared-counts-modal-wrap"!==d(this).attr("id")||d(a.target).is("#shared-counts-modal-wrap"))&&(a.preventDefault(),h())}),d(document).keyup(function(a){e&&27===a.keyCode&&h()}),d(document).on("click","#shared-counts-modal-submit",function(a){a.preventDefault();var e=!1,t=d(this),o=d("#shared-counts-modal-recipient"),s=d("#shared-counts-modal-name"),n=d("#shared-counts-modal-email"),c=d("#shared-counts-modal-validation"),i=d("#g-recaptcha-response"),r={action:"shared_counts_email",postid:l,recipient:o.val(),name:s.val(),email:n.val(),validation:c.val(),recaptcha:i.val(),nonce:u};d(o,s,n).each(function(){d(this).val()&&""!==d(this).val()||(e=!0)}),e?alert(shared_counts.email_fields_required):(d(this).prop("disabled",!0),d.post(shared_counts.ajaxurl,r,function(a){a.success&&(console.log(shared_counts.email_sent),h()),t.prop("disabled",!1)}).fail(function(a){console.log(a.responseText)}))})});
     1document.addEventListener("DOMContentLoaded",function(){document.addEventListener("click",function(t){var e=t.target;if(e.classList.contains("shared-counts-button")&&shared_counts.social_tracking){var a=e.getAttribute("data-social-network"),i=e.getAttribute("data-social-action"),n=e.getAttribute("data-social-target");a&&i&&n&&("function"==typeof ga?ga("send","social",a,i,n):"function"==typeof __gaTracker&&__gaTracker("send","social",a,i,n))}}),document.addEventListener("click",function(t){var e=t.target;if(e.classList.contains("shared-counts-button")&&"_blank"===e.getAttribute("target")&&!e.classList.contains("no-js")){t.preventDefault();var a="",i=e.href,n=i.split("/")[2];switch(n){case"www.facebook.com":a="width=585,height=368";break;case"twitter.com":a="width=585,height=261";break;case"pinterest.com":a="width=750,height=550";break;default:a="width=585,height=515"}window.open(i,"","menubar=no,toolbar=no,resizable=yes,scrollbars=yes,"+a),e.dispatchEvent(new Event("shared-counts-click"))}})});
  • shared-counts/trunk/includes/class-shared-counts-admin.php

    r2836328 r3023121  
    366366                                );
    367367                                ?>
    368                             </p>
    369                         </td>
    370                     </tr>
    371 
    372                     <!-- Enable Email reCAPTCHA (if email button is configured) -->
    373                     <tr valign="top" id="shared-counts-setting-row-recaptcha">
    374                         <th scope="row"><label for="shared-counts-setting-recaptcha"><?php esc_html_e( 'Enable Email reCAPTCHA', 'shared-counts' ); ?></label></th>
    375                         <td>
    376                             <input type="checkbox" name="shared_counts_options[recaptcha]" value="1" id="shared-counts-setting-recaptcha" <?php checked( $this->settings_value( 'recaptcha' ), 1 ); ?>>
    377                             <p class="description">
    378                                 <?php esc_html_e( 'Highly recommended, Google\'s v2 reCAPTCHA will protect the email sharing feature from abuse.', 'shared-counts' ); ?>
    379                             </p>
    380                         </td>
    381                     </tr>
    382 
    383                     <!-- Google reCAPTCHA Site key (if recaptcha is enabled) -->
    384                     <tr valign="top" id="shared-counts-setting-row-recaptcha_site_key">
    385                         <th scope="row"><label for="shared-counts-setting-recaptcha_site_key"><?php esc_html_e( 'reCAPTCHA Site Key', 'shared-counts' ); ?></label></th>
    386                         <td>
    387                             <input type="text" name="shared_counts_options[recaptcha_site_key]" value="<?php echo esc_attr( $this->settings_value( 'recaptcha_site_key' ) ); ?>" id="shared-counts-setting-recaptcha_site_key" class="regular-text" />
    388                             <p class="description">
    389                                 <?php esc_html_e( 'After signing up for Google\'s v2 reCAPTCHA (free), provide your site key here.', 'shared-counts' ); ?><br><a href="https://www.google.com/recaptcha/" target="_blank" rel="noopener noreferrer"><?php esc_html_e( 'Learn more.', 'shared-counts' ); ?></a>
    390                             </p>
    391                         </td>
    392                     </tr>
    393 
    394                     <!-- Google reCAPTCHA Secret key (if recaptcha is enabled) -->
    395                     <tr valign="top" id="shared-counts-setting-row-recaptcha_secret_key">
    396                         <th scope="row"><label for="shared-counts-setting-recaptcha_secret_key"><?php esc_html_e( 'reCAPTCHA Secret Key', 'shared-counts' ); ?></label></th>
    397                         <td>
    398                             <input type="text" name="shared_counts_options[recaptcha_secret_key]" value="<?php echo esc_attr( $this->settings_value( 'recaptcha_secret_key' ) ); ?>" id="shared-counts-setting-recaptcha_secret_key" class="regular-text" />
    399                             <p class="description">
    400                                 <?php esc_html_e( 'After signing up for Google\'s v2 reCAPTCHA (free), provide your secret key here.', 'shared-counts' ); ?>
    401368                            </p>
    402369                        </td>
     
    605572            'included_services'    => [ 'facebook', 'twitter', 'pinterest' ],
    606573            'query_services'       => [],
    607             'recaptcha'            => '',
    608             'recpatcha_site_key'   => '',
    609             'recaptcha_secret_key' => '',
    610574        ];
    611575    }
     
    692656        $input['theme_location']       = sanitize_text_field( $input['theme_location'] );
    693657        $input['included_services']    = ! empty( $input['included_services'] ) ? array_map( 'sanitize_text_field', $input['included_services'] ) : [];
    694         $input['recaptcha']            = ! empty( $input['recaptcha'] ) ? '1' : '';
    695         $input['recaptcha_site_key']   = sanitize_text_field( $input['recaptcha_site_key'] );
    696         $input['recaptcha_secret_key'] = sanitize_text_field( $input['recaptcha_secret_key'] );
    697 
    698658        return $input;
    699659    }
  • shared-counts/trunk/includes/class-shared-counts-core.php

    r2836327 r3023121  
    4242     */
    4343    public function __construct() {
    44 
    45         add_action( 'wp_ajax_shared_counts_email', [ $this, 'email_ajax' ] );
    46         add_action( 'wp_ajax_nopriv_shared_counts_email', [ $this, 'email_ajax' ] );
    4744        add_action( 'shutdown', [ $this, 'shutdown_update_share_counts' ] );
    48     }
    49 
    50     /**
    51      * Process and send email share AJAX requests.
    52      *
    53      * @since 1.0.0
    54      */
    55     public function email_ajax() {
    56 
    57         $data = $_POST; // phpcs:ignore
    58 
    59         // Check spam honeypot.
    60         if ( ! empty( $data['validation'] ) ) {
    61             wp_send_json_error( __( 'Honeypot triggered.', 'shared-counts' ) );
    62         }
    63 
    64         // Check required fields.
    65         if ( empty( $data['recipient'] ) || empty( $data['name'] ) || empty( $data['email'] ) ) {
    66             wp_send_json_error( __( 'Required field missing.', 'shared-counts' ) );
    67         }
    68 
    69         // Check email addresses.
    70         if ( ! is_email( $data['recipient'] ) || ! is_email( $data['email'] ) ) {
    71             wp_send_json_error( __( 'Invalid email.', 'shared-counts' ) );
    72         }
    73 
    74         $options = shared_counts()->admin->options();
    75 
    76         // Confirm email sharing is enabled.
    77         if ( ! in_array( 'email', $options['included_services'], true ) ) {
    78             wp_send_json_error( __( 'Email not enabled.', 'shared-counts' ) );
    79         }
    80 
    81         // Check if reCAPTCHA is enabled.
    82         $recaptcha = ! empty( $options['recaptcha'] ) && ! empty( $options['recaptcha_site_key'] ) && ! empty( $options['recaptcha_secret_key'] );
    83 
    84         // reCAPTCHA is enabled, so verify it.
    85         if ( $recaptcha ) {
    86 
    87             if ( empty( $data['recaptcha'] ) ) {
    88                 wp_send_json_error( __( 'reCAPTCHA is required.', 'shared-counts' ) );
    89             }
    90 
    91             $api_results = wp_remote_get( 'https://www.google.com/recaptcha/api/siteverify?secret=' . $options['recaptcha_secret_key'] . '&response=' . $data['recaptcha'] );
    92             $results     = json_decode( wp_remote_retrieve_body( $api_results ) );
    93             if ( empty( $results->success ) ) {
    94                 wp_send_json_error( __( 'Incorrect reCAPTCHA, please try again.', 'shared-counts' ) );
    95             }
    96         }
    97 
    98         $post_id    = absint( $data['postid'] );
    99         $recipient  = sanitize_text_field( $data['recipient'] );
    100         $from_email = sanitize_text_field( $data['email'] );
    101         $from_name  = sanitize_text_field( $data['name'] );
    102         $site_name  = html_entity_decode( wp_strip_all_tags( get_bloginfo( 'name' ) ), ENT_QUOTES );
    103         $site_root  = strtolower( $_SERVER['SERVER_NAME'] ); //phpcs:ignore
    104         if ( substr( $site_root, 0, 4 ) === 'www.' ) {
    105             $site_root = substr( $site_root, 4 );
    106         }
    107 
    108         $headers = [
    109             sprintf( 'From: %s <noreply@%s>', $site_name, $site_root ),
    110             sprintf( 'Reply-To: %s <%s>', $from_name, $from_email ),
    111         ];
    112         /* translators: %1$s - Name of the person who shared the article. */
    113         $subject = sprintf( esc_html__( 'Your friend %1$s has shared an article with you.', 'shared-counts' ), $from_name );
    114         $body    = html_entity_decode( get_the_title( $post_id ), ENT_QUOTES ) . "\r\n";
    115         $body   .= get_permalink( $post_id ) . "\r\n";
    116 
    117         wp_mail(
    118             $recipient,
    119             apply_filters( 'shared_counts_email_subject', $subject, $post_id, $recipient, $from_name, $from_email ),
    120             apply_filters( 'shared_counts_email_body',    $body,    $post_id, $recipient, $from_name, $from_email ),
    121             apply_filters( 'shared_counts_email_headers', $headers, $post_id, $recipient, $from_name, $from_email )
    122         );
    123 
    124         // Don't track email shares if plugin is configured to omit counts.
    125         if ( ! empty( $options['count_source'] ) && 'none' !== $options['count_source'] ) {
    126             $count  = absint( get_post_meta( $post_id, 'shared_counts_email', true ) );
    127             $update = update_post_meta( $post_id, 'shared_counts_email', ++$count );
    128         }
    129 
    130         wp_send_json_success();
    13145    }
    13246
  • shared-counts/trunk/includes/class-shared-counts-front.php

    r2727404 r3023121  
    4242        add_action( 'wp_enqueue_scripts', [ $this, 'header_assets' ], 9 );
    4343        add_action( 'wp_footer', [ $this, 'load_assets' ], 1 );
    44         add_action( 'wp_footer', [ $this, 'email_modal' ], 50 );
    4544        add_shortcode( 'shared_counts', [ $this, 'shortcode' ] );
    4645        add_action( 'admin_bar_menu', [ $this, 'admin_bar' ], 999 );
     
    150149            'shared-counts',
    151150            SHARED_COUNTS_URL . 'assets/js/shared-counts' . $suffix . '.js',
    152             [ 'jquery' ],
     151            [],
    153152            SHARED_COUNTS_VERSION,
    154153            true
     
    180179        }
    181180
    182         $options   = shared_counts()->admin->options();
    183         $recaptcha = ! empty( $options['recaptcha'] ) && ! empty( $options['recaptcha_site_key'] ) && ! empty( $options['recaptcha_secret_key'] );
    184 
    185181        // Load CSS.
    186182        if ( apply_filters( 'shared_counts_load_css', true ) ) {
     
    193189            wp_enqueue_script( 'shared-counts' );
    194190
    195             if ( $recaptcha ) {
    196                 wp_enqueue_script(
    197                     'recaptcha',
    198                     'https://www.google.com/recaptcha/api.js',
    199                     [],
    200                     '2.0',
    201                     true
    202                 );
    203             }
    204191        }
    205192
    206193        // Localize JS strings.
    207194        $args = [
    208             'email_fields_required' => esc_html__( 'Please complete out all 3 fields to email this article.', 'shared-counts' ),
    209             'email_sent'            => esc_html__( 'Article successfully shared.', 'shared-counts' ),
    210             'ajaxurl'               => esc_url_raw( admin_url( 'admin-ajax.php' ) ),
    211195            'social_tracking'       => apply_filters( 'shared_counts_social_tracking', true ),
    212196        ];
    213197
    214         // Localize recaptcha site key if enabled.
    215         if ( $recaptcha ) {
    216             $args['recaptchaSitekey'] = sanitize_text_field( $options['recaptcha_site_key'] );
    217         }
    218 
    219198        wp_localize_script( 'shared-counts', 'shared_counts', $args );
    220     }
    221 
    222     /**
    223      * Email modal pop-up.
    224      *
    225      * This popup is output (and hidden) in the site footer if the Email
    226      * service is configured in the plugin settings.
    227      *
    228      * @since 1.0.0
    229      */
    230     public function email_modal() {
    231 
    232         // Only continue if a share link is on the page.
    233         if ( ! $this->share_link ) {
    234             return;
    235         }
    236 
    237         // Filter to disable email modal
    238         if( apply_filters( 'shared_counts_disable_email_modal', false ) ) {
    239             return;
    240         }
    241 
    242         // Check to see the email button is configured or being overriden. The
    243         // filter can be used to enable the modal in use cases where the share
    244         // button is manually being called.
    245         $options = shared_counts()->admin->options();
    246 
    247         if ( ! in_array( 'email', $options['included_services'], true ) && ! apply_filters( 'shared_counts_email_modal', false ) ) {
    248             return;
    249         }
    250 
    251         // Check for reCAPTCHA settings.
    252         $recaptcha = ! empty( $options['recaptcha'] ) && ! empty( $options['recaptcha_site_key'] ) && ! empty( $options['recaptcha_secret_key'] );
    253 
    254         // Labels, filterable of course.
    255         $labels = apply_filters(
    256             'shared_counts_email_labels',
    257             [
    258                 'title'      => esc_html__( 'Share this Article', 'shared-counts' ),
    259                 'subtitle'   => esc_html__( 'Like this article? Email it to a friend!', 'shared-counts' ),
    260                 'title_icon' => '<svg xmlns="http://www.w3.org/2000/svg" width="48" height="45" viewBox="0 0 48 45"><path fill-rule="evenodd" d="M31.7849302,27.028093 L27.9750698,27.028093 L27.9750698,19.6751628 C27.9750698,18.7821395 27.9940465,17.9650233 28.0331163,17.2249302 C27.7986977,17.5095814 27.5062326,17.8065116 27.1579535,18.1179535 L25.5806512,19.4195349 L23.6327442,17.024 L28.4026047,13.1393488 L31.7849302,13.1393488 L31.7849302,27.028093 Z M22.6392558,21.4422326 L19.104,21.4422326 L19.104,24.8714419 L16.5488372,24.8714419 L16.5488372,21.4422326 L13.015814,21.4422326 L13.015814,18.896 L16.5488372,18.896 L16.5488372,15.4098605 L19.104,15.4098605 L19.104,18.896 L22.6392558,18.896 L22.6392558,21.4422326 Z M43.5996279,2 L4.40037209,2 C1.9735814,2 0,3.97469767 0,6.40037209 L0,32.8003721 C0,35.2260465 1.9735814,37.1996279 4.40037209,37.1996279 L22.3791628,37.1996279 L33.2796279,45.92 C33.5843721,46.1633488 33.9505116,46.2883721 34.3211163,46.2883721 C34.5689302,46.2883721 34.8178605,46.2325581 35.0511628,46.119814 C35.636093,45.8385116 36,45.2591628 36,44.6106047 L36,37.1996279 L43.5996279,37.1996279 C46.0253023,37.1996279 48,35.2260465 48,32.8003721 L48,6.40037209 C48,3.97469767 46.0253023,2 43.5996279,2 Z" transform="translate(0 -2)"/></svg>',
    261                 'recipient'  => esc_html__( 'Friend\'s Email Address', 'shared-counts' ),
    262                 'name'       => esc_html__( 'Your Name', 'shared-counts' ),
    263                 'email'      => esc_html__( 'Your Email Address', 'shared-counts' ),
    264                 'validation' => esc_html__( 'Comments', 'shared-counts' ),
    265                 'close'      => '<span class="close-icon"><svg xmlns="http://www.w3.org/2000/svg" width="8" height="8" viewBox="0 0 8 8">
    266                 <path fill="#FFF" fill-rule="evenodd" d="M338,11.0149385 L340.805644,8.20929447 C341.000906,8.01403233 341.317489,8.01403233 341.512751,8.20929447 L341.790706,8.48724919 C341.985968,8.68251134 341.985968,8.99909383 341.790706,9.19435597 L338.985062,12 L341.790706,14.805644 C341.985968,15.0009062 341.985968,15.3174887 341.790706,15.5127508 L341.512751,15.7907055 C341.317489,15.9859677 341.000906,15.9859677 340.805644,15.7907055 L338,12.9850615 L335.194356,15.7907055 C334.999094,15.9859677 334.682511,15.9859677 334.487249,15.7907055 L334.209294,15.5127508 C334.014032,15.3174887 334.014032,15.0009062 334.209294,14.805644 L337.014938,12 L334.209294,9.19435597 C334.014032,8.99909383 334.014032,8.68251134 334.209294,8.48724919 L334.487249,8.20929447 C334.682511,8.01403233 334.999094,8.01403233 335.194356,8.20929447 L338,11.0149385 Z" transform="translate(-334 -8)"/>
    267                 </svg></span>',
    268                 'submit'     => esc_html__( 'Send Email', 'shared-counts' ),
    269             ]
    270         );
    271         ?>
    272         <div id="shared-counts-modal-wrap" style="display:none;">
    273             <div class="shared-counts-modal">
    274                 <a href="#" id="shared-counts-modal-close" aria-label="Close the share by email popup"><?php echo $labels['close']; // phpcs:ignore ?></a>
    275                 <div class="shared-counts-modal-header">
    276                     <?php
    277                     if ( ! empty( $labels['title_icon'] ) ) {
    278                         echo '<span class="shared-counts-modal-icon">' . $labels['title_icon'] . '</span>'; // phpcs:ignore
    279                     }
    280                     if ( ! empty( $labels['title'] ) ) {
    281                         echo '<span class="shared-counts-modal-title">' . esc_html( $labels['title'] ) . '</span>';
    282                     }
    283                     if ( ! empty( $labels['subtitle'] ) ) {
    284                         echo '<span class="shared-counts-modal-subtitle">' . esc_html( $labels['subtitle'] ) . '</span>';
    285                     }
    286                     ?>
    287                 </div>
    288                 <div class="shared-counts-modal-content">
    289                     <p>
    290                         <label for="shared-counts-modal-recipient"><?php echo esc_html( $labels['recipient'] ); ?></label>
    291                         <input type="email" id="shared-counts-modal-recipient" placeholder="<?php echo esc_html( $labels['recipient'] ); ?>">
    292                     </p>
    293                     <p>
    294                         <label for="shared-counts-modal-name"><?php echo esc_html( $labels['name'] ); ?></label>
    295                         <input type="text" id="shared-counts-modal-name" placeholder="<?php echo esc_html( $labels['name'] ); ?>">
    296                     </p>
    297                     <p>
    298                         <label for="shared-counts-modal-email"><?php echo esc_html( $labels['email'] ); ?></label>
    299                         <input type="email" id="shared-counts-modal-email" placeholder="<?php echo esc_html( $labels['email'] ); ?>">
    300                     </p>
    301                     <?php
    302                     if ( $recaptcha ) {
    303                         echo '<div id="shared-counts-modal-recaptcha"></div>';
    304                     }
    305                     ?>
    306                     <p class="shared-counts-modal-validation">
    307                         <label for="shared-counts-modal-validation"><?php echo esc_html( $labels['validation'] ); ?></label>
    308                         <input type="text" id="shared-counts-modal-validation" autocomplete="off">
    309                     </p>
    310                     <p class="shared-counts-modal-submit">
    311                         <button id="shared-counts-modal-submit"><?php echo $labels['submit']; // phpcs:ignore ?></button>
    312                     </p>
    313                     <div id="shared-counts-modal-sent"><?php esc_html_e( 'Email sent!', 'shared-counts' ); ?></div>
    314                 </div>
    315             </div>
    316         </div>
    317         <?php
    318199    }
    319200
     
    537418                    $link['link']           = 'https://twitter.com/share?url=' . $link['url'] . '&text=' . htmlspecialchars( rawurlencode( html_entity_decode( $link['title'], ENT_COMPAT, 'UTF-8' ) ), ENT_COMPAT, 'UTF-8' );
    538419                    $link['label']          = esc_html__( 'Tweet', 'shared-counts' );
    539                     $link['icon']           = '<svg version="1.1" xmlns="http://www.w3.org/2000/svg" width="29.71875" height="32" viewBox="0 0 951 1024"><path d="M925.714 233.143q-38.286 56-92.571 95.429 0.571 8 0.571 24 0 74.286-21.714 148.286t-66 142-105.429 120.286-147.429 83.429-184.571 31.143q-154.857 0-283.429-82.857 20 2.286 44.571 2.286 128.571 0 229.143-78.857-60-1.143-107.429-36.857t-65.143-91.143q18.857 2.857 34.857 2.857 24.571 0 48.571-6.286-64-13.143-106-63.714t-42-117.429v-2.286q38.857 21.714 83.429 23.429-37.714-25.143-60-65.714t-22.286-88q0-50.286 25.143-93.143 69.143 85.143 168.286 136.286t212.286 56.857q-4.571-21.714-4.571-42.286 0-76.571 54-130.571t130.571-54q80 0 134.857 58.286 62.286-12 117.143-44.571-21.143 65.714-81.143 101.714 53.143-5.714 106.286-28.571z"></path></svg>';
     420                    $link['icon']           = '<svg viewBox="0 0 24 24" xmlns="http://www.w3.org/2000/svg"><path d="M13.8944 10.4695L21.3345 2H19.5716L13.1085 9.35244L7.95022 2H1.99936L9.80147 13.1192L1.99936 22H3.76218L10.5832 14.2338L16.0318 22H21.9827L13.8944 10.4695ZM11.4792 13.2168L10.6875 12.1089L4.39789 3.30146H7.10594L12.1833 10.412L12.9717 11.5199L19.5708 20.7619H16.8628L11.4792 13.2168Z" /></svg>';
    540421                    $link['target']         = '_blank';
    541422                    $link['rel']            = 'nofollow noopener noreferrer';
     
    588469                    break;
    589470                case 'email':
    590                     $link['link']           = '#shared-counts-email';
     471                    $subject                = esc_html__( 'Your friend has shared an article with you.', 'shared-counts' );
     472                    $subject                = apply_filters( 'shared_counts_amp_email_subject', $subject, $id );
     473                    $body                   = html_entity_decode( get_the_title( $id ), ENT_QUOTES ) . "\r\n";
     474                    $body                  .= get_permalink( $id ) . "\r\n";
     475                    $body                   = apply_filters( 'shared_counts_amp_email_body', $body, $id );
     476                    $link['link']           = 'mailto:?subject=' . rawurlencode( $subject ) . '&body=' . rawurlencode( $body );
    591477                    $link['label']          = esc_html__( 'Email', 'shared-counts' );
    592478                    $link['icon']           = '<svg version="1.1" xmlns="http://www.w3.org/2000/svg" width="32" height="32" viewBox="0 0 1024 1024"><path d="M1024 405.714v453.714q0 37.714-26.857 64.571t-64.571 26.857h-841.143q-37.714 0-64.571-26.857t-26.857-64.571v-453.714q25.143 28 57.714 49.714 206.857 140.571 284 197.143 32.571 24 52.857 37.429t54 27.429 62.857 14h1.143q29.143 0 62.857-14t54-27.429 52.857-37.429q97.143-70.286 284.571-197.143 32.571-22.286 57.143-49.714zM1024 237.714q0 45.143-28 86.286t-69.714 70.286q-214.857 149.143-267.429 185.714-5.714 4-24.286 17.429t-30.857 21.714-29.714 18.571-32.857 15.429-28.571 5.143h-1.143q-13.143 0-28.571-5.143t-32.857-15.429-29.714-18.571-30.857-21.714-24.286-17.429q-52-36.571-149.714-104.286t-117.143-81.429q-35.429-24-66.857-66t-31.429-78q0-44.571 23.714-74.286t67.714-29.714h841.143q37.143 0 64.286 26.857t27.143 64.571z"></path></svg>';
     
    683569                $elements['wrap_open']  = sprintf(
    684570                    '<a href="%s"%s%s%s class="%s"%s>',
    685                     esc_attr( $link['link'] ),
     571                    esc_url( $link['link'] ),
    686572                    $attr_title,
    687573                    $target,
  • shared-counts/trunk/readme.txt

    r2980287 r3023121  
    11=== Shared Counts - Social Media Share Buttons ===
    2 Contributors: jaredatch, billerickson, cdils
     2Contributors: jaredatch, billerickson
    33Tags: sharing, share buttons, social buttons, share counts, social, facebook, linkedin, pinterest, twitter
    44Requires at least: 4.6
    5 Tested up to: 6.1
    6 Stable tag: 1.4.1
     5Tested up to: 6.4
     6Stable tag: 1.5.0
    77Requires PHP: 5.6
    88License: GPLv2 or later
     
    2020- Yummly Button
    2121- Twitter Tweet Button (using the third-party Twitcount.com API)
    22 - Email Sharing (with reCAPTCHA support to prevent abuse)
     22- Email Sharing
    2323- Share Count Total
    2424- Print Button
     
    7575If you would like to include Twitter share counts, you can sign up for a free account at [twitcount.com](https://twitcount.com).
    7676
    77 If you use the Email share button, we recommend you enable Google's reCAPTCHA to prevent spam. [Sign up here](https://www.google.com/recaptcha/intro/android.html) (free) to get your Site Key and Secret Key.
    78 
    7977== Screenshots ==
    8078
     
    8280
    8381== Changelog ==
     82
     83**1.5.0**
     84- Changed: Replaced server side email with a mailto link to remove the possibility of spam, see #131
     85- Changed: Updated the Twitter logo to X, see #129
     86- Changed: Removed jQuery dependency, see #113
     87- Changed: Use `esc_url()` for sharing links, see #130
    8488
    8589**1.4.0**
  • shared-counts/trunk/shared-counts.php

    r2836327 r3023121  
    55 * Description: Social sharing buttons that look great and keep your site loading fast.
    66 * Author:      Bill Erickson & Jared Atchison
    7  * Version:     1.4.1
     7 * Version:     1.5.0
    88 *
    99 * Shared Counts is free software: you can redistribute it and/or modify
     
    3636 */
    3737// Version.
    38 define( 'SHARED_COUNTS_VERSION', '1.4.1' );
     38define( 'SHARED_COUNTS_VERSION', '1.5.0' );
    3939
    4040// Directory path.
Note: See TracChangeset for help on using the changeset viewer.