Skip to:
Content

BuddyPress.org


Ignore:
Timestamp:
07/10/2016 09:46:27 PM (10 years ago)
Author:
djpaul
Message:

Emails: add unsubscribe feature.

Updates the unsubscribe token to link to a new unsubscribe action handler.

All emails from other platforms or popular websites have a unsubscribe link. For previous versions of BuddyPress, our unsubscribe link pointed to that user's notifications page. However, if someone creates an account on a BuddyPress with a fraudulent email address (for example), that email address' owner will not know the account's authentication details, and so have no way to unsubscribe from that "spam" email.

The change implements a new action handler which accepts unsubscription requests from un-authenticated requests. It adds an new option containing a dynamically generated salt which is used to generate the hash on the unsubscribe link.

Fixes #6932

Props tharsheblows, DJPaul

File:
1 edited

Legend:

Unmodified
Added
Removed
  • trunk/src/bp-core/bp-core-functions.php

    r10899 r10941  
    33703370 *
    33713371 * @since 2.5.1
    3372  *
    3373  * @return array
    3374  */
    3375 function bp_email_get_type_schema() {
    3376         return array(
    3377                 'activity-comment'                   => __( 'A member has replied to an activity update that the recipient posted.', 'buddypress' ),
    3378                 'activity-comment-author'            => __( 'A member has replied to a comment on an activity update that the recipient posted.', 'buddypress' ),
    3379                 'activity-at-message'                => __( 'Recipient was mentioned in an activity update.', 'buddypress' ),
    3380                 'groups-at-message'                  => __( 'Recipient was mentioned in a group activity update.', 'buddypress' ),
    3381                 'core-user-registration'             => __( 'Recipient has registered for an account.', 'buddypress' ),
    3382                 'core-user-registration-with-blog'   => __( 'Recipient has registered for an account and site.', 'buddypress' ),
    3383                 'friends-request'                    => __( 'A member has sent a friend request to the recipient.', 'buddypress' ),
    3384                 'friends-request-accepted'           => __( 'Recipient has had a friend request accepted by a member.', 'buddypress' ),
    3385                 'groups-details-updated'             => __( "A group's details were updated.", 'buddypress' ),
    3386                 'groups-invitation'                  => __( 'A member has sent a group invitation to the recipient.', 'buddypress' ),
    3387                 'groups-member-promoted'             => __( "Recipient's status within a group has changed.", 'buddypress' ),
    3388                 'groups-membership-request'          => __( 'A member has requested permission to join a group.', 'buddypress' ),
    3389                 'messages-unread'                    => __( 'Recipient has received a private message.', 'buddypress' ),
    3390                 'settings-verify-email-change'       => __( 'Recipient has changed their email address.', 'buddypress' ),
    3391                 'groups-membership-request-accepted' => __( 'Recipient had requested to join a group, which was accepted.', 'buddypress' ),
    3392                 'groups-membership-request-rejected' => __( 'Recipient had requested to join a group, which was rejected.', 'buddypress' ),
     3372 * @since 2.7.0 $field argument added.
     3373 *
     3374 * @param string $field Optional; defaults to "description" for backwards compatibility. Other values: "all".
     3375 * @return array {
     3376 *     The array of email types and their schema.
     3377 *
     3378 *     @type string $description The description of the action which causes this to trigger.
     3379 *     @type array  $unsubscribe {
     3380 *         Replacing this with false indicates that a user cannot unsubscribe from this type.
     3381 *
     3382 *         @type string $meta_key The meta_key used to toggle the email setting for this notification.
     3383 *         @type string $message  The message shown when the user has successfully unsubscribed.
     3384 *     }
     3385 */
     3386function bp_email_get_type_schema( $field = 'description' ) {
     3387        $activity_comment = array(
     3388                'description'   => __( 'A member has replied to an activity update that the recipient posted.', 'buddypress' ),
     3389                'unsubscribe'   => array(
     3390                        'meta_key'      => 'notification_activity_new_reply',
     3391                        'message'       => __( 'You will no longer receive emails when someone replies to an update or comment you posted.', 'buddypress' ),
     3392                        ),
    33933393        );
    3394 }
     3394
     3395        $activity_comment_author = array(
     3396                'description'   => __( 'A member has replied to a comment on an activity update that the recipient posted.', 'buddypress' ),
     3397                'unsubscribe'   => array(
     3398                        'meta_key'      => 'notification_activity_new_reply',
     3399                        'message'       => __( 'You will no longer receive emails when someone replies to an update or comment you posted.', 'buddypress' ),
     3400                        ),
     3401        );
     3402
     3403        $activity_at_message = array(
     3404                'description'   => __( 'Recipient was mentioned in an activity update.', 'buddypress' ),
     3405                'unsubscribe'   => array(
     3406                        'meta_key'      => 'notification_activity_new_mention',
     3407                        'message'       => __( 'You will no longer receive emails when someone mentions you in an update.', 'buddypress' ),
     3408                ),
     3409        );
     3410
     3411        $groups_at_message = array(
     3412                'description'   => __( 'Recipient was mentioned in a group activity update.', 'buddypress' ),
     3413                'unsubscribe'   => array(
     3414                        'meta_key'      => 'notification_activity_new_mention',
     3415                        'message'       => __( 'You will no longer receive emails when someone mentions you in an update.', 'buddypress' ),
     3416                ),
     3417        );
     3418
     3419        $core_user_registration = array(
     3420                'description'   => __( 'Recipient has registered for an account.', 'buddypress' ),
     3421                'unsubscribe'   => false,
     3422        );
     3423
     3424        $core_user_registration_with_blog = array(
     3425                'description'   => __( 'Recipient has registered for an account and site.', 'buddypress' ),
     3426                'unsubscribe'   => false,
     3427        );
     3428
     3429        $friends_request = array(
     3430                'description'   => __( 'A member has sent a friend request to the recipient.', 'buddypress' ),
     3431                'unsubscribe'   => array(
     3432                        'meta_key'      => 'notification_friends_friendship_request',
     3433                        'message'       => __( 'You will no longer receive emails when someone sends you a friend request.', 'buddypress' ),
     3434                ),
     3435        );
     3436
     3437        $friends_request_accepted = array(
     3438                'description'   => __( 'Recipient has had a friend request accepted by a member.', 'buddypress' ),
     3439                'unsubscribe'   => array(
     3440                        'meta_key'      => 'notification_friends_friendship_accepted',
     3441                        'message'       => __( 'You will no longer receive emails when someone accepts your friendship request.', 'buddypress' ),
     3442                ),
     3443        );
     3444
     3445        $groups_details_updated = array(
     3446                'description'   => __( "A group's details were updated.", 'buddypress' ),
     3447                'unsubscribe'   => array(
     3448                        'meta_key'      => 'notification_groups_group_updated',
     3449                        'message'       => __( 'You will no longer receive emails when one of your groups is updated.', 'buddypress' ),
     3450                ),
     3451        );
     3452
     3453        $groups_details_updated = array(
     3454                'description'   => __( "A group's details were updated.", 'buddypress' ),
     3455                'unsubscribe'   => array(
     3456                        'meta_key'      => 'notification_groups_group_updated',
     3457                        'message'       => __( 'You will no longer receive emails when one of your groups is updated.', 'buddypress' ),
     3458                ),
     3459        );
     3460
     3461        $groups_invitation = array(
     3462                'description'   => __( 'A member has sent a group invitation to the recipient.', 'buddypress' ),
     3463                'unsubscribe'   => array(
     3464                        'meta_key'      => 'notification_groups_invite',
     3465                        'message'       => __( 'You will no longer receive emails when you are invited to join a group.', 'buddypress' ),
     3466                ),
     3467        );
     3468
     3469        $groups_member_promoted = array(
     3470                'description'   => __( "Recipient's status within a group has changed.", 'buddypress' ),
     3471                'unsubscribe'   => array(
     3472                        'meta_key'      => 'notification_groups_admin_promotion',
     3473                        'message'       => __( 'You will no longer receive emails when you have been promoted in a group.', 'buddypress' ),
     3474                ),
     3475        );
     3476
     3477        $groups_member_promoted = array(
     3478                'description'   => __( "Recipient's status within a group has changed.", 'buddypress' ),
     3479                'unsubscribe'   => array(
     3480                        'meta_key'      => 'notification_groups_admin_promotion',
     3481                        'message'       => __( 'You will no longer receive emails when you have been promoted in a group.', 'buddypress' ),
     3482                ),
     3483        );
     3484
     3485        $groups_membership_request = array(
     3486                'description'   => __( 'A member has requested permission to join a group.', 'buddypress' ),
     3487                'unsubscribe'   => array(
     3488                        'meta_key'      => 'notification_groups_membership_request',
     3489                        'message'       => __( 'You will no longer receive emails when someone requests to be a member of your group.', 'buddypress' ),
     3490                ),
     3491        );
     3492
     3493        $messages_unread = array(
     3494                'description'   => __( 'Recipient has received a private message.', 'buddypress' ),
     3495                'unsubscribe'   => array(
     3496                        'meta_key'      => 'notification_messages_new_message',
     3497                        'message'       => __( 'You will no longer receive emails when someone sends you a message.', 'buddypress' ),
     3498                ),
     3499        );
     3500
     3501        $settings_verify_email_change = array(
     3502                'description'   => __( 'Recipient has changed their email address.', 'buddypress' ),
     3503                'unsubscribe'   => false,
     3504        );
     3505
     3506        $groups_membership_request_accepted = array(
     3507                'description'   => __( 'Recipient had requested to join a group, which was accepted.', 'buddypress' ),
     3508                'unsubscribe'   => array(
     3509                        'meta_key'      => 'notification_membership_request_completed',
     3510                        'message'       => __( 'You will no longer receive emails when your request to join a group has been accepted or denied.', 'buddypress' ),
     3511                ),
     3512        );
     3513
     3514        $groups_membership_request_rejected = array(
     3515                'description'   => __( 'Recipient had requested to join a group, which was rejected.', 'buddypress' ),
     3516                'unsubscribe'   => array(
     3517                        'meta_key'      => 'notification_membership_request_completed',
     3518                        'message'       => __( 'You will no longer receive emails when your request to join a group has been accepted or denied.', 'buddypress' ),
     3519                ),
     3520        );
     3521
     3522        $types = array(
     3523                'activity-comment'                   => $activity_comment,
     3524                'activity-comment-author'            => $activity_comment_author,
     3525                'activity-at-message'                => $activity_at_message,
     3526                'groups-at-message'                  => $groups_at_message,
     3527                'core-user-registration'             => $core_user_registration,
     3528                'core-user-registration-with-blog'   => $core_user_registration_with_blog,
     3529                'friends-request'                    => $friends_request,
     3530                'friends-request-accepted'           => $friends_request_accepted,
     3531                'groups-details-updated'             => $groups_details_updated,
     3532                'groups-invitation'                  => $groups_invitation,
     3533                'groups-member-promoted'             => $groups_member_promoted,
     3534                'groups-membership-request'          => $groups_membership_request,
     3535                'messages-unread'                    => $messages_unread,
     3536                'settings-verify-email-change'       => $settings_verify_email_change,
     3537                'groups-membership-request-accepted' => $groups_membership_request_accepted,
     3538                'groups-membership-request-rejected' => $groups_membership_request_rejected,
     3539        );
     3540
     3541        if ( $field !== 'all' ) {
     3542                return wp_list_pluck( $types, $field );
     3543        } else {
     3544                return $types;
     3545        }
     3546}
     3547
     3548/**
     3549 * Handles unsubscribing user from notification emails.
     3550 *
     3551 * @since 2.7.0
     3552 */
     3553function bp_email_unsubscribe_handler() {
     3554        $emails         = bp_email_get_type_schema( 'all' );
     3555        $raw_email_type = ! empty( $_GET['nt'] ) ? $_GET['nt'] : '';
     3556        $raw_hash       = ! empty( $_GET['nh'] ) ? $_GET['nh'] : '';
     3557        $raw_user_id    = ! empty( $_GET['uid'] ) ? absint( $_GET['uid'] ) : 0;
     3558        $new_hash       = hash_hmac( 'sha1', "{$raw_email_type}:{$raw_user_id}", bp_email_get_salt() );
     3559
     3560        // Check required values.
     3561        if ( ! $raw_user_id || ! $raw_email_type || ! $raw_hash || ! array_key_exists( $raw_email_type, $emails ) ) {
     3562                $redirect_to = site_url( 'wp-login.php' );
     3563                $result_msg  = __( 'Something has gone wrong.', 'buddypress' );
     3564                $unsub_msg   = __( 'Please log in and go to your settings to unsubscribe from notification emails.', 'buddypress' );
     3565
     3566        // Check valid hash.
     3567        } elseif ( ! hash_equals( $new_hash, $raw_hash ) ) {
     3568                $redirect_to = site_url( 'wp-login.php' );
     3569                $result_msg  = __( 'Something has gone wrong.', 'buddypress' );
     3570                $unsub_msg   = __( 'Please log in and go to your settings to unsubscribe from notification emails.', 'buddypress' );
     3571
     3572        // Don't let authenticated users unsubscribe other users' email notifications.
     3573        } elseif ( is_user_logged_in() && get_current_user_id() !== $raw_user_id ) {
     3574                $result_msg  = __( 'Something has gone wrong.', 'buddypress' );
     3575                $unsub_msg   = __( 'Please go to your notifications settings to unsubscribe from emails.', 'buddypress' );
     3576
     3577                if ( bp_is_active( 'settings' ) ) {
     3578                        $redirect_to = sprintf(
     3579                                '%s%s/notifications/',
     3580                                bp_core_get_user_domain( get_current_user_id() ),
     3581                                bp_get_settings_slug()
     3582                        );
     3583                } else {
     3584                        $redirect_to = bp_core_get_user_domain( get_current_user_id() );
     3585                }
     3586
     3587        } else {
     3588                if ( bp_is_active( 'settings' ) ) {
     3589                        $redirect_to = sprintf(
     3590                                '%s%s/notifications/',
     3591                                bp_core_get_user_domain( $raw_user_id ),
     3592                                bp_get_settings_slug()
     3593                        );
     3594                } else {
     3595                        $redirect_to = bp_core_get_user_domain( $raw_user_id );
     3596                }
     3597
     3598                // Unsubscribe.
     3599                $meta_key = $emails[ $raw_email_type ]['unsubscribe']['meta_key'];
     3600                bp_update_user_meta( $raw_user_id, $meta_key, 'no' );
     3601
     3602                $result_msg = $emails[ $raw_email_type ]['unsubscribe']['message'];
     3603                $unsub_msg  = __( 'You can change this or any other email notification preferences in your email settings.', 'buddypress' );
     3604        }
     3605
     3606        $message = sprintf(
     3607                '%1$s <a href="%2$s">%3$s</a>',
     3608                $result_msg,
     3609                esc_url( $redirect_to ),
     3610                esc_html( $unsub_msg )
     3611        );
     3612
     3613        bp_core_add_message( $message );
     3614        bp_core_redirect( bp_core_get_user_domain( $raw_user_id ) );
     3615
     3616        exit;
     3617}
     3618
     3619/**
     3620 * Creates unsubscribe link for notification emails.
     3621 *
     3622 * @since 2.7.0
     3623 *
     3624 * @param string $redirect_to The URL to which the unsubscribe query string is appended.
     3625 * @param array $args {
     3626 *    Used to build unsubscribe query string.
     3627 *
     3628 *    @type string $notification_type Which notification type is being sent.
     3629 *    @type string $user_id           The ID of the user to whom the notification is sent.
     3630 *    @type string $redirect_to       Optional. The url to which the user will be redirected. Default is the activity directory.
     3631 * }
     3632 * @return string The unsubscribe link.
     3633 */
     3634function bp_email_get_unsubscribe_link( $args ) {
     3635        $emails = bp_email_get_type_schema( 'all' );
     3636
     3637        if ( empty( $args['notification_type'] ) || ! array_key_exists( $args['notification_type'], $emails ) ) {
     3638                return site_url( 'wp-login.php' );
     3639        }
     3640
     3641        $email_type  = $args['notification_type'];
     3642        $redirect_to = ! empty( $args['redirect_to'] ) ? $args['redirect_to'] : site_url();
     3643        $user_id     = (int) $args['user_id'];
     3644
     3645        // Bail out if the activity type is not un-unsubscribable.
     3646        if ( empty( $emails[ $email_type ]['unsubscribe'] ) ) {
     3647                return '';
     3648        }
     3649
     3650        $link = add_query_arg(
     3651                array(
     3652                        'action' => 'unsubscribe',
     3653                        'nh'     => hash_hmac( 'sha1', "{$email_type}:{$user_id}", bp_email_get_salt() ),
     3654                        'nt'     => $args['notification_type'],
     3655                        'uid'    => $user_id,
     3656                ),
     3657                $redirect_to
     3658        );
     3659
     3660        /**
     3661         * Filters the unsubscribe link.
     3662         *
     3663         * @since 2.7.0
     3664         */
     3665        return apply_filters( 'bp_email_get_link', $link, $redirect_to, $args );
     3666}
     3667
     3668/**
     3669 * Get a persistent salt for email unsubscribe links.
     3670 *
     3671 * @since 2.7.0
     3672 *
     3673 * @return string|null Returns null if value isn't set, otherwise string.
     3674 */
     3675function bp_email_get_salt() {
     3676        return bp_get_option( 'bp-emails-unsubscribe-salt', null );
     3677}
Note: See TracChangeset for help on using the changeset viewer.