Skip to:
Content

BuddyPress.org

Ticket #8582: 8582.9-combined.patch

File 8582.9-combined.patch, 79.2 KB (added by dcavins, 3 years ago)

Complete patch with all changes up to now.

  • src/bp-core/admin/bp-core-admin-settings.php

    diff --git src/bp-core/admin/bp-core-admin-settings.php src/bp-core/admin/bp-core-admin-settings.php
    index 023d2cea4..e2f4cffcc 100644
    function bp_admin_setting_callback_members_invitations() { 
    203203        do_action( 'bp_admin_settings_after_members_invitations' );
    204204}
    205205
     206/**
     207 * Allow new users to request membership to the network.
     208 *
     209 * @since 10.0.0
     210 */
     211function bp_admin_setting_callback_membership_requests() {
     212?>
     213        <input id="bp-enable-membership-requests" name="bp-enable-membership-requests" type="checkbox" value="1" <?php checked( bp_get_membership_requests_required( 'raw' ) ); ?> <?php if ( bp_get_signup_allowed() ) { echo 'disabled="disabled"'; } ?> />
     214        <label for="bp-enable-membership-requests"><?php esc_html_e( 'Allow visitors to request site membership. If enabled, an administrator must approve each new site membership request.', 'buddypress' ); ?></label>
     215        <?php if ( bp_get_signup_allowed() ) : ?>
     216                <p class="description"><?php esc_html_e( 'Public registration is currently enabled. If you wish to require approval for new memberships, disable public registration and enable the membership requests feature.', 'buddypress' ); ?></p>
     217        <?php endif; ?>
     218        <?php
     219        /**
     220         * Fires after the output of the membership requests settings section.
     221         *
     222         * @since 10.0.0
     223         */
     224        do_action( 'bp_admin_settings_after_membership_requests' );
     225}
     226
    206227/** XProfile ******************************************************************/
    207228
    208229/**
  • new file src/bp-core/admin/js/bp-thickbox.js

    diff --git src/bp-core/admin/js/bp-thickbox.js src/bp-core/admin/js/bp-thickbox.js
    new file mode 100644
    index 000000000..0bafd38fa
    - +  
     1/**
     2 * Improves the Thickbox library for BuddyPress needs.
     3 *
     4 * @since 10.0.0
     5 */
     6( function( $ ) {
     7        window.bpAdjustThickbox = function( label, padding ) {
     8                $( '#TB_window' ).attr( {
     9                                                        'role': 'dialog',
     10                                                        'aria-label': label
     11                                                } )
     12                                                .addClass( 'plugin-details-modal' )
     13                                                .removeClass( 'thickbox-loading' );
     14
     15
     16                if ( ! padding ) {
     17                        padding = 0;
     18                }
     19
     20                $( '#TB_ajaxContent' ).prop( 'style', 'height: 100%; width: auto; padding: ' + padding + '; border: none;' );
     21
     22                try {
     23                        var tabbables = $( ':tabbable', '#TB_ajaxContent' ), lastTabbable = tabbables.last();
     24
     25                        // Move the focus to the Modal's close button once the last Hello link was tabbed out.
     26                        $( '#TB_window' ).on( 'keydown', function( event ) {
     27                                var keyCode;
     28
     29                                if ( event.key !== undefined ) {
     30                                        keyCode = event.key;
     31                                } else {
     32                                        // event.keyCode is deprecated.
     33                                        keyCode = event.keyCode;
     34                                }
     35
     36                                if ( 9 === keyCode && ! event.shiftKey && $( lastTabbable ).prop( 'classList' ).value === $( event.target ).prop( 'classList' ).value ) {
     37                                        event.preventDefault();
     38
     39                                        $( '#TB_closeWindowButton' ).trigger( 'focus' );
     40                                }
     41
     42                                if ( 9 === keyCode && event.shiftKey && 'TB_closeWindowButton' === $( event.target ).prop( 'id' ) ) {
     43                                        event.preventDefault();
     44
     45                                        $( lastTabbable ).trigger( 'focus' );
     46                                }
     47                        } );
     48                } catch ( error ) {
     49                        return;
     50                }
     51        };
     52} ( jQuery ) );
  • src/bp-core/admin/js/hello.js

    diff --git src/bp-core/admin/js/hello.js src/bp-core/admin/js/hello.js
    index 47c996579..77461a559 100644
     
    1919                }
    2020
    2121                window.tb_show( 'BuddyPress', '#TB_inline?inlineId=bp-hello-container' );
    22 
    23                 $( '#TB_window' ).attr( {
    24                                                         'role': 'dialog',
    25                                                         'aria-label': bpHelloStrings.modalLabel
    26                                                 } )
    27                                                 .addClass( 'plugin-details-modal' )
    28                                                 .removeClass( 'thickbox-loading' );
    29 
    30                 $( '#TB_ajaxContent' ).prop( 'style', 'height: 100%; width: auto; padding: 0; border: none;' );
    31 
    32                 var tabbables = $( ':tabbable', '#TB_ajaxContent' ), lastTabbable = tabbables.last();
    33 
    34                 // Move the focus to the Modal's close button once the last Hello link was tabbed out.
    35                 $( '#TB_window' ).on( 'keydown', function( event ) {
    36                         if ( 9 === event.keyCode && ! event.shiftKey && $( lastTabbable ).prop( 'classList' ).value === $( event.target ).prop( 'classList' ).value ) {
    37                                 event.preventDefault();
    38 
    39                                 $( '#TB_closeWindowButton' ).focus();
    40                         }
    41 
    42                         if ( 9 === event.keyCode && event.shiftKey && 'TB_closeWindowButton' === $( event.target ).prop( 'id' ) ) {
    43                                 event.preventDefault();
    44 
    45                                 $( lastTabbable ).focus();
    46                         }
    47                 } );
     22                window.bpAdjustThickbox( bpHelloStrings.modalLabel );
    4823        };
    4924
    5025        /**
  • src/bp-core/bp-core-filters.php

    diff --git src/bp-core/bp-core-filters.php src/bp-core/bp-core-filters.php
    index 85a8978d2..b41c54410 100644
    function bp_core_activation_signup_blog_notification( $domain, $path, $title, $u 
    473473                ),
    474474        );
    475475
    476         $signups = BP_Signup::get(
    477                 array(
    478                         'user_login' => $user,
    479                 )
    480         );
    481 
     476        $signup     = bp_members_get_signup_by( 'activation_key', $key );
    482477        $salutation = $user;
    483         if ( $signups && bp_is_active( 'xprofile' ) ) {
    484                 $signup = $signups['signups'][0];
     478        if ( $signup && bp_is_active( 'xprofile' ) ) {
    485479                if ( isset( $signup->meta[ 'field_' . bp_xprofile_fullname_field_id() ] ) ) {
    486480                        $salutation = $signup->meta[ 'field_' . bp_xprofile_fullname_field_id() ];
    487481                }
    488482        }
    489483
    490         bp_send_email( 'core-user-registration-with-blog', array( array( $user_email => $salutation ) ), $args );
     484        /**
     485         * Filters if BuddyPress should send an activation key for a new multisite signup.
     486         *
     487         * @since 10.0.0
     488         *
     489         * @param string $user       The user's login name.
     490         * @param string $user_email The user's email address.
     491         * @param string $key        The activation key created in wpmu_signup_blog().
     492         * @param string $domain     The new blog domain.
     493         * @param string $path       The new blog path.
     494         * @param string $title      The site title.
     495         */
     496        if ( apply_filters( 'bp_core_signup_send_activation_key_multisite_blog', true, $user, $user_email, $key, $domain, $path, $title ) ) {
     497
     498                bp_send_email( 'core-user-registration-with-blog', array( array( $user_email => $salutation ) ), $args );
     499
     500        }
    491501
    492502        // Return false to stop the original WPMU function from continuing.
    493503        return false;
    function bp_core_activation_signup_user_notification( $user, $user_email, $key, 
    556566                        'user.id'      => $user_id,
    557567                ),
    558568        );
    559         bp_send_email( 'core-user-registration', array( array( $user_email => $salutation ) ), $args );
     569
     570        /**
     571         * Filters if BuddyPress should send an activation key for a new multisite signup.
     572         *
     573         * @since 10.0.0
     574         *
     575         * @param string $user       The user's login name.
     576         * @param string $user_email The user's email address.
     577         * @param string $key        The activation key created in wpmu_signup_blog().
     578         */
     579        if ( apply_filters( 'bp_core_signup_send_activation_key_multisite', true, $user, $user_email, $key ) ) {
     580
     581                bp_send_email( 'core-user-registration', array( array( $user_email => $salutation ) ), $args );
     582
     583        }
     584
    560585
    561586        // Return false to stop the original WPMU function from continuing.
    562587        return false;
    563588}
    564589add_filter( 'wpmu_signup_user_notification', 'bp_core_activation_signup_user_notification', 1, 4 );
    565590
     591/**
     592 * Ensure that some meta values are set for new multisite signups.
     593 *
     594 * @since 10.0.0
     595 *
     596 * @see wpmu_signup_user() for a full description of params.
     597 *
     598 * @param array $meta Signup meta data. Default empty array.
     599 * @return array Signup meta data.
     600 */
     601function bp_core_add_meta_to_multisite_signups( $meta ) {
     602
     603        // Ensure that sent_date and count_sent are set in meta.
     604        if ( ! isset( $meta['sent_date'] ) ) {
     605                $meta['sent_date'] = '0000-00-00 00:00:00';
     606        }
     607        if ( ! isset( $meta['count_sent'] ) ) {
     608                $meta['count_sent'] = 0;
     609        }
     610
     611        return $meta;
     612}
     613add_filter( 'signup_user_meta', 'bp_core_add_meta_to_multisite_signups' );
     614
    566615/**
    567616 * Filter the page title for BuddyPress pages.
    568617 *
  • src/bp-core/bp-core-functions.php

    diff --git src/bp-core/bp-core-functions.php src/bp-core/bp-core-functions.php
    index 0e66f4cb7..fd938861f 100644
    function bp_core_replace_tokens_in_text( $text, $tokens ) { 
    37923792 * Get a list of emails for populating the email post type.
    37933793 *
    37943794 * @since 2.5.1
     3795 * @since 10.0.0 Added members-membership-request and
     3796 *               members-membership-request-rejected email types.
    37953797 *
    37963798 * @return array
    37973799 */
    function bp_email_get_schema() { 
    39523954                        /* translators: do not remove {} brackets or translate its contents. */
    39533955                        'post_excerpt' => __( "{{inviter.name}} has invited you to join the site \"{{site.name}}\".\n\n{{usermessage}}\n\nTo accept your invitation, visit: {{{invite.accept_url}}}\n\nTo learn more about the site, visit: {{{site.url}}}.\nTo view {{inviter.name}}'s profile, visit: {{{inviter.url}}}", 'buddypress' ),
    39543956                ),
     3957                'members-membership-request' => array(
     3958                        /* translators: do not remove {} brackets or translate its contents. */
     3959                        'post_title'   => __( '{{requesting-user.user_login}} would like to join {{site.name}}', 'buddypress' ),
     3960                        /* translators: do not remove {} brackets or translate its contents. */
     3961                        'post_content' => __( "{{requesting-user.user_login}} would like to join the site: &quot;{{site.name}}&quot;.\n\n<a href=\"{{{manage.url}}}\">Manage the request</a>.", 'buddypress' ),
     3962                        /* translators: do not remove {} brackets or translate its contents. */
     3963                        'post_excerpt' => __( "{{requesting-user.user_login}} would like to join the site \"{{site.name}}\".\n\nTo manage the request, visit: {{{manage.url}}}.", 'buddypress' ),
     3964                ),
     3965                'members-membership-request-rejected' => array(
     3966                        /* translators: do not remove {} brackets or translate its contents. */
     3967                        'post_title'   => __( 'Your request to join {{site.name}} has been declined', 'buddypress' ),
     3968                        /* translators: do not remove {} brackets or translate its contents. */
     3969                        'post_content' => __( "Sorry, your request to join the site &quot;{{site.name}}&quot; has been declined.", 'buddypress' ),
     3970                        /* translators: do not remove {} brackets or translate its contents. */
     3971                        'post_excerpt' => __( "Sorry, your request to join the site \"{{site.name}}\" has been declined.", 'buddypress' ),
     3972                ),
    39553973        ) );
    39563974}
    39573975
    function bp_email_get_type_schema( $field = 'description' ) { 
    41244142                ),
    41254143        );
    41264144
     4145        $members_membership_request= array(
     4146                'description'      => __( 'Someone has requested membership on this site.', 'buddypress' ),
     4147                'named_salutation' => true,
     4148                'unsubscribe'      => array(
     4149                        'meta_key' => 'notification_members_membership_request',
     4150                        'message'  => __( 'You will no longer receive emails when people submit requests to join this site.', 'buddypress' ),
     4151                ),
     4152        );
     4153
     4154        $members_membership_request_rejected= array(
     4155                'description'      => __( 'A site membership request has been rejected.', 'buddypress' ),
     4156                'named_salutation' => false,
     4157                'unsubscribe'      => false,
     4158        );
     4159
    41274160        $types = array(
    4128                 'activity-comment'                   => $activity_comment,
    4129                 'activity-comment-author'            => $activity_comment_author,
    4130                 'activity-at-message'                => $activity_at_message,
    4131                 'groups-at-message'                  => $groups_at_message,
    4132                 'core-user-registration'             => $core_user_registration,
    4133                 'core-user-registration-with-blog'   => $core_user_registration_with_blog,
    4134                 'friends-request'                    => $friends_request,
    4135                 'friends-request-accepted'           => $friends_request_accepted,
    4136                 'groups-details-updated'             => $groups_details_updated,
    4137                 'groups-invitation'                  => $groups_invitation,
    4138                 'groups-member-promoted'             => $groups_member_promoted,
    4139                 'groups-membership-request'          => $groups_membership_request,
    4140                 'messages-unread'                    => $messages_unread,
    4141                 'settings-verify-email-change'       => $settings_verify_email_change,
    4142                 'groups-membership-request-accepted' => $groups_membership_request_accepted,
    4143                 'groups-membership-request-rejected' => $groups_membership_request_rejected,
    4144                 'core-user-activation'               => $core_user_activation,
    4145                 'bp-members-invitation'              => $members_invitation,
     4161                'activity-comment'                    => $activity_comment,
     4162                'activity-comment-author'             => $activity_comment_author,
     4163                'activity-at-message'                 => $activity_at_message,
     4164                'groups-at-message'                   => $groups_at_message,
     4165                'core-user-registration'              => $core_user_registration,
     4166                'core-user-registration-with-blog'    => $core_user_registration_with_blog,
     4167                'friends-request'                     => $friends_request,
     4168                'friends-request-accepted'            => $friends_request_accepted,
     4169                'groups-details-updated'              => $groups_details_updated,
     4170                'groups-invitation'                   => $groups_invitation,
     4171                'groups-member-promoted'              => $groups_member_promoted,
     4172                'groups-membership-request'           => $groups_membership_request,
     4173                'messages-unread'                     => $messages_unread,
     4174                'settings-verify-email-change'        => $settings_verify_email_change,
     4175                'groups-membership-request-accepted'  => $groups_membership_request_accepted,
     4176                'groups-membership-request-rejected'  => $groups_membership_request_rejected,
     4177                'core-user-activation'                => $core_user_activation,
     4178                'bp-members-invitation'               => $members_invitation,
     4179                'members-membership-request'          => $members_membership_request,
     4180                'members-membership-request-rejected' => $members_membership_request_rejected,
    41464181        );
    41474182
    41484183        if ( $field !== 'all' ) {
  • src/bp-core/bp-core-template.php

    diff --git src/bp-core/bp-core-template.php src/bp-core/bp-core-template.php
    index a88cd6800..9ef914ed9 100644
    function bp_get_title_parts( $seplocation = 'right' ) { 
    31923192
    31933193        // Sign up page.
    31943194        } elseif ( bp_is_register_page() ) {
    3195                 $bp_title_parts = array( __( 'Create an Account', 'buddypress' ) );
     3195                if ( bp_get_membership_requests_required() ) {
     3196                        $bp_title_parts = array( __( 'Request Membership', 'buddypress' ) );
     3197                } else {
     3198                        $bp_title_parts = array( __( 'Create an Account', 'buddypress' ) );
     3199                }
    31963200
    31973201        // Activation page.
    31983202        } elseif ( bp_is_activation_page() ) {
  • src/bp-core/bp-core-update.php

    diff --git src/bp-core/bp-core-update.php src/bp-core/bp-core-update.php
    index 03710213c..6468ace39 100644
    function bp_version_updater() { 
    278278                if ( $raw_db_version < 12850 ) {
    279279                        bp_update_to_8_0();
    280280                }
     281
     282                // Version 10.0.0.
     283                if ( $raw_db_version < 13150 ) {
     284                        bp_update_to_10_0();
     285                }
     286
    281287        }
    282288
    283289        /* All done! *************************************************************/
    function bp_core_get_8_0_upgrade_email_schema( $emails ) { 
    704710        return $new_emails;
    705711}
    706712
     713/**
     714 * 10.0.0 update routine.
     715 *
     716 * - Install new BP Emails for membership requests.
     717 *
     718 * @since 10.0.0
     719 */
     720function bp_update_to_10_0() {
     721
     722        // Install membership request emails.
     723        add_filter( 'bp_email_get_schema', 'bp_core_get_10_0_upgrade_email_schema' );
     724
     725        bp_core_install_emails();
     726
     727        remove_filter( 'bp_email_get_schema', 'bp_core_get_10_0_upgrade_email_schema' );
     728}
     729
     730/**
     731 * Select only the emails that need to be installed with version 10.0.
     732 *
     733 * @since 10.0.0
     734 *
     735 * @param array $emails The array of emails schema.
     736 */
     737function bp_core_get_10_0_upgrade_email_schema( $emails ) {
     738        $new_emails = array();
     739
     740        if ( isset( $emails['members-membership-request'] ) ) {
     741                $new_emails['members-membership-request'] = $emails['members-membership-request'];
     742        }
     743
     744        if ( isset( $emails['members-membership-request-rejected'] ) ) {
     745                $new_emails['members-membership-request-rejected'] = $emails['members-membership-request-rejected'];
     746        }
     747
     748        return $new_emails;
     749}
     750
    707751/**
    708752 * Updates the component field for new_members type.
    709753 *
  • src/bp-core/classes/class-bp-admin.php

    diff --git src/bp-core/classes/class-bp-admin.php src/bp-core/classes/class-bp-admin.php
    index 9b7a87508..0fe584430 100644
    class BP_Admin { 
    482482                        register_setting( 'buddypress', 'bp-enable-members-invitations', 'intval' );
    483483                }
    484484
     485                // Membership requests.
     486                if ( bp_is_active( 'members', 'membership_requests' ) ) {
     487                        add_settings_field( 'bp-enable-membership-requests', __( 'Membership Requests', 'buddypress' ), 'bp_admin_setting_callback_membership_requests', 'buddypress', 'bp_members' );
     488                        register_setting( 'buddypress', 'bp-enable-membership-requests', 'intval' );
     489                }
     490
    485491                /* XProfile Section **************************************************/
    486492
    487493                if ( bp_is_active( 'xprofile' ) ) {
    class BP_Admin { 
    12821288                                'footer'       => true,
    12831289                        ),
    12841290
     1291                        // 10.0
     1292                        'bp-thickbox' => array(
     1293                                'file'         => "{$url}bp-thickbox{$min}.js",
     1294                                'dependencies' => array( 'thickbox' ),
     1295                                'footer'       => true,
     1296                        ),
     1297
    12851298                        // 3.0
    12861299                        'bp-hello-js' => array(
    12871300                                'file'         => "{$url}hello{$min}.js",
    1288                                 'dependencies' => array( 'thickbox', 'wp-api-request' ),
     1301                                'dependencies' => array( 'bp-thickbox', 'wp-api-request' ),
    12891302                                'footer'       => true,
    12901303                        ),
    12911304                ) );
  • src/bp-core/classes/class-bp-invitation-manager.php

    diff --git src/bp-core/classes/class-bp-invitation-manager.php src/bp-core/classes/class-bp-invitation-manager.php
    index d92a76b7a..718384e7d 100644
    abstract class BP_Invitation_Manager { 
    227227         *         @type int    $item_id ID associated with the invitation and class.
    228228         *         @type int    $secondary_item_id secondary ID associated with the
    229229         *                              invitation and class.
    230          *         @type string $type @TODO. < missing description.
     230         *         @type string $type    Type of record this is: 'invite' or 'request'.
    231231         *         @type string $content Extra information provided by the requester
    232232         *                              or inviter.
    233233         *         @type string $date_modified Date the invitation was last modified.
  • new file src/bp-members/admin/js/signup-preview.js

    diff --git src/bp-members/admin/js/signup-preview.js src/bp-members/admin/js/signup-preview.js
    new file mode 100644
    index 000000000..15c200ea7
    - +  
     1/* global bpSignupPreview */
     2/**
     3 * Opens a modal to preview a signup/membership request.
     4 *
     5 * @since 10.0.0
     6 */
     7( function( $ ) {
     8        // Bail if not set or if Thickbox is not available.
     9        if ( typeof bpSignupPreview === 'undefined' || 'function' !== typeof window.tb_show ) {
     10                return;
     11        }
     12
     13        $( function() {
     14                $( '.bp-thickbox' ).on( 'click', function( e ) {
     15                        e.preventDefault();
     16
     17                        var fragment = $( e.target ).prop( 'href' ).split( '#TB_inline&' )[1];
     18
     19                        window.tb_show( 'BuddyPress', '#TB_inline?' + fragment );
     20                        window.bpAdjustThickbox( bpSignupPreview.modalLabel, '1em' );
     21                } );
     22        } );
     23}( jQuery ) );
  • src/bp-members/bp-members-admin.php

    diff --git src/bp-members/bp-members-admin.php src/bp-members/bp-members-admin.php
    index 1b80fe190..3aa4e01d7 100644
    function bp_members_type_admin_updated_messages( $messages = array() ) { 
    112112        return $messages;
    113113}
    114114add_filter( 'term_updated_messages', 'bp_members_type_admin_updated_messages' );
     115
     116/**
     117 * Formats xprofile field data about a signup/membership request for display.
     118 *
     119 * Operates recursively on arrays, which are then imploded with commas.
     120 *
     121 * @since 10.0.0
     122 *
     123 * @param string|array $value Field value.
     124 */
     125function bp_members_admin_format_xprofile_field_for_display( $value ) {
     126        if ( is_array( $value ) ) {
     127                $value = array_map( 'bp_signup_format_xprofile_field_for_display', $value );
     128                $value = implode( ', ', $value );
     129        } else {
     130                $value = stripslashes( $value );
     131                $value = esc_html( $value );
     132        }
     133
     134        return $value;
     135}
     136
     137/**
     138 * Outputs Informations about a signup/membership request into a modal inside the Signups Admin Screen.
     139 *
     140 * @since 10.0.0
     141 *
     142 * @param array $signup_field_labels The Signup field labels.
     143 * @param object|null $signup_object The signup data object.
     144 */
     145function bp_members_admin_preview_signup_profile_info( $signup_field_labels = array(), $signup_object = null ) {
     146        if ( ! isset( $signup_object->meta ) || ! $signup_field_labels ) {
     147                return;
     148        }
     149
     150        // Init ids.
     151        $profile_field_ids = array();
     152
     153        // Get all xprofile field IDs except field 1.
     154        if ( ! empty( $signup_object->meta['profile_field_ids'] ) ) {
     155                $profile_field_ids = array_flip( explode( ',', $signup_object->meta['profile_field_ids'] ) );
     156                unset( $profile_field_ids[1] );
     157        }
     158        ?>
     159        <div id="signup-info-modal-<?php echo $signup_object->id; ?>" style="display:none;">
     160                <h1><?php printf( '%1$s (%2$s)', esc_html( $signup_object->user_name ), esc_html( $signup_object->user_email ) ); ?></h1>
     161                <h2><?php esc_html_e( 'Extended Profile Information', 'buddypress' ); ?></h2>
     162
     163                <table class="signup-profile-data-drawer wp-list-table widefat fixed striped">
     164                        <?php if ( 1 <= count( $profile_field_ids ) ): foreach ( array_keys( $profile_field_ids ) as $profile_field_id ) :
     165                                $field_value = isset( $signup_object->meta[ "field_{$profile_field_id}" ] ) ? $signup_object->meta[ "field_{$profile_field_id}" ] : ''; ?>
     166                                <tr>
     167                                        <td class="column-fields"><?php echo esc_html( $signup_field_labels[ $profile_field_id ] ); ?></td>
     168                                        <td><?php echo bp_members_admin_format_xprofile_field_for_display( $field_value ); ?></td>
     169                                </tr>
     170                        <?php endforeach; else:  ?>
     171                                <tr>
     172                                        <td><?php esc_html_e( 'There is no additional information to display.', 'buddypress' ); ?></td>
     173                                </tr>
     174                        <?php endif; ?>
     175                </table>
     176        </div>
     177        <?php
     178}
  • src/bp-members/bp-members-functions.php

    diff --git src/bp-members/bp-members-functions.php src/bp-members/bp-members-functions.php
    index 93fd7cafd..e61bdf632 100644
    function bp_core_signup_send_validation_email( $user_id, $user_email, $key, $sal 
    23882388        bp_send_email( 'core-user-registration', $to, $args );
    23892389
    23902390        // Record that the activation email has been sent.
    2391         $signups = BP_Signup::get(
    2392                 array(
    2393                         'activation_key' => $key,
    2394                 )
    2395         );
     2391        $signup = bp_members_get_signup_by( 'activation_key', $key );
    23962392
    2397         if ( ! empty( $signups['signups'] ) ) {
    2398                 foreach ( $signups['signups'] as $signup ) {
    2399                         $meta = array(
    2400                                 'sent_date'  => current_time( 'mysql', true ),
    2401                                 'count_sent' => $signup->count_sent + 1
    2402                         );
     2393        if ( $signup ) {
     2394                $meta = array(
     2395                        'sent_date'  => current_time( 'mysql', true ),
     2396                        'count_sent' => $signup->count_sent + 1
     2397                );
    24032398
    2404                         BP_Signup::update( array(
    2405                                 'signup_id' => $signup->id,
    2406                                 'meta'      => $meta,
    2407                         ) );
    2408                 }
     2399                BP_Signup::update( array(
     2400                        'signup_id' => $signup->id,
     2401                        'meta'      => $meta,
     2402                ) );
    24092403        }
    24102404}
    24112405
    function bp_core_signup_disable_inactive( $user = null, $username = '', $passwor 
    24502444        }
    24512445
    24522446        // Unactivated user account found!
    2453         // Set up the feedback message.
    2454         $signup_id = $signup['signups'][0]->signup_id;
    2455 
    2456         $resend_url_params = array(
    2457                 'action' => 'bp-resend-activation',
    2458                 'id'     => $signup_id,
    2459         );
     2447        /*
     2448         * Don't allow users to resend their own activation email
     2449         * when membership requests are enabled.
     2450         */
     2451        if ( bp_get_membership_requests_required() ) {
     2452                $error_message = sprintf(
     2453                        '<strong>%1$s</strong> %2$s',
     2454                        esc_html_x( 'Error:', 'Warning displayed on the WP Login screen', 'buddypress' ),
     2455                        esc_html_x( 'Your membership request has not yet been approved.', 'Error message displayed on the WP Login screen', 'buddypress' )
     2456                );
     2457        } else {
     2458                // Set up the feedback message.
     2459                $signup_id = $signup['signups'][0]->signup_id;
    24602460
    2461         $resend_url = wp_nonce_url(
    2462                 add_query_arg( $resend_url_params, wp_login_url() ),
    2463                 'bp-resend-activation'
    2464         );
     2461                $resend_url_params = array(
     2462                        'action' => 'bp-resend-activation',
     2463                        'id'     => $signup_id,
     2464                );
    24652465
    2466         $resend_string = '<br /><br />';
     2466                $resend_url = wp_nonce_url(
     2467                        add_query_arg( $resend_url_params, wp_login_url() ),
     2468                        'bp-resend-activation'
     2469                );
    24672470
    2468         /* translators: %s: the activation url */
    2469         $resend_string .= sprintf( __( 'If you have not received an email yet, <a href="%s">click here to resend it</a>.', 'buddypress' ), esc_url( $resend_url ) );
     2471                $error_message = sprintf(
     2472                        '<strong>%1$s</strong> %2$s<br /><br />%3$s',
     2473                        esc_html_x( 'Error:', 'Warning displayed on the WP Login screen', 'buddypress' ),
     2474                        esc_html_x( 'Your account has not been activated. Check your email for the activation link.', 'Error message displayed on the WP Login screen', 'buddypress' ),
     2475                        sprintf(
     2476                                /* translators: %s: the link to resend the activation email. */
     2477                                esc_html_x( 'If you have not received an email yet, %s.', 'WP Login screen message', 'buddypress' ),
     2478                                sprintf(
     2479                                        '<a href="%1$s">%2$s</a>',
     2480                                        esc_url( $resend_url ),
     2481                                        esc_html_x( 'click here to resend it', 'Text of the link to resend the activation email', 'buddypress' )
     2482                                )
     2483                        )
     2484                );
     2485        }
    24702486
    2471         return new WP_Error( 'bp_account_not_activated', __( '<strong>Error</strong>: Your account has not been activated. Check your email for the activation link.', 'buddypress' ) . $resend_string );
     2487        return new WP_Error( 'bp_account_not_activated', $error_message );
    24722488}
    24732489add_filter( 'authenticate', 'bp_core_signup_disable_inactive', 30, 3 );
    24742490
    function bp_get_members_invitation_from_request() { 
    36943710         */
    36953711        return apply_filters( 'bp_get_members_invitation_from_request', $invite );
    36963712}
     3713
     3714/**
     3715 * Get WP_User object corresponding to a record in the signups table.
     3716 *
     3717 * @since 10.0.0
     3718 *
     3719 * @param string $field Which fields to search by. Possible values are
     3720 *                      activation_key, user_email, id.
     3721 * @param string $value Value to search by.
     3722 * @return bool|BP_Signup $signup Found signup, returns first found
     3723 *                                if more than one is found.
     3724 */
     3725function bp_members_get_signup_by( $field = 'activation_key', $value = '' ) {
     3726        switch ( $field ) {
     3727                case 'activation_key':
     3728                case 'user_email':
     3729                        $key = $field;
     3730                        break;
     3731
     3732                case 'id':
     3733                default:
     3734                        $key = 'include';
     3735                        break;
     3736        }
     3737
     3738        $signups = BP_Signup::get(
     3739                array(
     3740                        $key => $value,
     3741                )
     3742        );
     3743
     3744        if ( ! empty( $signups['signups'] ) ) {
     3745                $signup = current( $signups['signups'] );
     3746        } else {
     3747                $signup = false;
     3748        }
     3749
     3750        return $signup;
     3751}
  • src/bp-members/bp-members-invitations.php

    diff --git src/bp-members/bp-members-invitations.php src/bp-members/bp-members-invitations.php
    index 73f50e09b..2fadc0e94 100644
    function bp_members_invitations_delete_optedout_invites( $optout ) { 
    183183        );
    184184}
    185185add_action( 'bp_optout_after_save', 'bp_members_invitations_delete_optedout_invites' );
     186
     187/**
     188 * If a user submits a site membership request, but there's a
     189 * sent invitation to her, bypass the manual approval of the request.
     190 *
     191 * @since 10.0.0
     192 *
     193 * @param bool  $send    Whether or not this membership request should be approved
     194 *                       immediately and the activation email sent.
     195 *                       Default is `false` meaning that the request should be
     196 *                       manually approved by a site admin.
     197 * @param array $details The details of the request.
     198 */
     199function bp_members_invitations_maybe_bypass_request_approval( $send, $details ) {
     200        if ( ! bp_get_members_invitations_allowed() ) {
     201                return $send;
     202        }
     203
     204        // We'll need the prospective user's email address.
     205        if ( empty( $details['user_email'] ) ) {
     206                return $send;
     207        }
     208
     209        $invites = bp_members_invitations_get_invites(
     210                array(
     211                        'invitee_email' => $details['user_email'],
     212                        'invite_sent'   => 'sent'
     213                )
     214        );
     215
     216        // If pending invitations exist, send the verification mail.
     217        if ( $invites ) {
     218                $send = true;
     219        }
     220
     221        return $send;
     222}
     223add_filter( 'bp_members_membership_requests_bypass_manual_approval', 'bp_members_invitations_maybe_bypass_request_approval', 10, 2 );
     224add_filter( 'bp_members_membership_requests_bypass_manual_approval_multisite', 'bp_members_invitations_maybe_bypass_request_approval', 10, 2 );
  • new file src/bp-members/bp-members-membership-requests.php

    diff --git src/bp-members/bp-members-membership-requests.php src/bp-members/bp-members-membership-requests.php
    new file mode 100644
    index 000000000..a55f71726
    - +  
     1<?php
     2/**
     3 * BuddyPress Membership Requests
     4 *
     5 * @package BuddyPress
     6 * @subpackage MembersMembershipRequest
     7 * @since 10.0.0
     8 */
     9
     10// Exit if accessed directly.
     11defined( 'ABSPATH' ) || exit;
     12
     13/**
     14 * Single site: When a user creates a membership request,
     15 * prevent the sending of the activation email so that
     16 * the site admins can send it manually.
     17 *
     18 * @since 10.0.0
     19 *
     20 * @param bool   $send           Whether or not to send the activation key.
     21 * @param int    $user_id        User ID to send activation key to.
     22 * @param string $user_email     User email to send activation key to.
     23 * @param string $activation_key Activation key to be sent.
     24 * @param array  $usermeta       Miscellaneous metadata about the user (blog-specific
     25 *                               signup data, xprofile data, etc).
     26 * @return bool Whether or not to send the activation key.
     27 */
     28function bp_members_membership_requests_cancel_activation_email( $send, $user_id = 0, $user_email = '', $activation_key = '', $usermeta = array() ) {
     29
     30        $details = array(
     31                'user_id'        => $user_id,
     32                'user_email'     => $user_email,
     33                'activation_key' => $activation_key,
     34                'usermeta'       => $usermeta,
     35        );
     36
     37        /**
     38         * Allow some membership requests to be approved immediately.
     39         * For example, you might want to approve all requests
     40         * coming from users with certain email address domains.
     41         * If `true` is returned the activation email will be sent to the user.
     42         *
     43         * @since 10.0.0
     44         *
     45         * @param bool  $send    Whether or not this membership request should be approved
     46         *                       immediately and the activation email sent.
     47         *                       Default is `false` meaning that the request should be
     48         *                       manually approved by a site admin.
     49         * @param array $details The details of the request.
     50         */
     51        $send = apply_filters( 'bp_members_membership_requests_bypass_manual_approval', false, $details );
     52
     53        // If the registration process has been interrupted, this is a new membership request.
     54        if ( ! $send ) {
     55                $signup = bp_members_get_signup_by( 'activation_key', $activation_key );
     56
     57                /**
     58                 * Fires when a site membership request has been created and is pending.
     59                 *
     60                 * @since 10.0.0
     61                 *
     62                 * @param BP_Signup $signup  The signup object that has been created.
     63                 * @param array     $details The details of the request.
     64                 */
     65                do_action( 'bp_members_membership_request_submitted', $signup, $details );
     66        }
     67
     68        return $send;
     69}
     70add_filter( 'bp_core_signup_send_activation_key', 'bp_members_membership_requests_cancel_activation_email', 10, 5 );
     71
     72/**
     73 * WP Multisite: When a user creates a membership request,
     74 * prevent the sending of the activation email so that
     75 * the site admins can send it manually.
     76 *
     77 * @since 10.0.0
     78 *
     79 * @param bool   $send           Whether or not to send the activation key.
     80 * @param string $user_login     User login name.
     81 * @param string $user_email     User email address.
     82 * @param string $activation_key Activation key created in wpmu_signup_user().
     83 * @return bool Whether or not to send the activation key.
     84 */
     85function bp_members_membership_requests_cancel_activation_email_multisite( $send = true, $user_login = '', $user_email = '', $activation_key = '' ) {
     86
     87        $details = array(
     88                'user_login'     => $user_login,
     89                'user_email'     => $user_email,
     90                'activation_key' => $activation_key,
     91        );
     92
     93        /**
     94         * Allow some membership requests to be approved immediately.
     95         * For example, you might want to approve all requests
     96         * coming from users with certain email address domains.
     97         * If `true` is returned the activation email will be sent to the user.
     98         *
     99         * @since 10.0.0
     100         *
     101         * @param bool  $send    Whether or not this membership request should be approved
     102         *                       immediately and the activation email sent.
     103         *                       Default is `false` meaning that the request should be
     104         *                       manually approved by a site admin.
     105         * @param array $details The details of the request.
     106         */
     107        $send = apply_filters( 'bp_members_membership_requests_bypass_manual_approval_multisite', false, $details );
     108
     109        // If the registration process has been interrupted, this is a new membership request.
     110        if ( ! $send ) {
     111                $signup = bp_members_get_signup_by( 'activation_key', $activation_key );
     112
     113                /**
     114                 * Fires when a site membership request has been created and is pending.
     115                 *
     116                 * @since 10.0.0
     117                 *
     118                 * @param BP_Signup $signup  The signup object that has been created.
     119                 * @param array     $details The details of the request.
     120                 */
     121                do_action( 'bp_members_membership_request_submitted', $signup, $details );
     122        }
     123
     124        return $send;
     125}
     126add_filter( 'bp_core_signup_send_activation_key_multisite', 'bp_members_membership_requests_cancel_activation_email_multisite', 10, 4 );
     127add_filter( 'bp_core_signup_send_activation_key_multisite_blog', 'bp_members_membership_requests_cancel_activation_email_multisite', 10, 4 );
     128
     129/**
     130 * Notifications
     131 *********************************************************************/
     132
     133/**
     134 * Notify site admins about a new membership request.
     135 *
     136 * @since 10.0.0
     137 *
     138 * @param BP_Signup $signup  The signup object that has been created.
     139 */
     140function bp_members_membership_requests_notify_site_admins( $signup ) {
     141
     142        if ( ! isset( $signup->signup_id ) ) {
     143                return;
     144        }
     145
     146        // Notify all site admins so the request can be handled.
     147        $admin_ids = get_users(
     148                array(
     149                        'fields' => 'ids',
     150                        'role'   => 'administrator',
     151                )
     152        );
     153
     154        foreach ( $admin_ids as $admin_id ) {
     155                // Trigger a BuddyPress Notification.
     156                if ( bp_is_active( 'notifications' ) ) {
     157                        bp_notifications_add_notification(
     158                                array(
     159                                        'user_id'          => $admin_id,
     160                                        'item_id'          => $signup->signup_id,
     161                                        'component_name'   => buddypress()->members->id,
     162                                        'component_action' => 'membership_request_submitted',
     163                                        'date_notified'    => bp_core_current_time(),
     164                                        'is_new'           => 1,
     165                                )
     166                        );
     167                }
     168
     169                // Bail if member opted out of receiving this email.
     170                if ( 'no' === bp_get_user_meta( $admin_id, 'notification_members_membership_request', true ) ) {
     171                        return;
     172                }
     173
     174                $unsubscribe_args = array(
     175                        'user_id'           => $admin_id,
     176                        'notification_type' => 'members-membership-request',
     177                );
     178
     179                $manage_url = add_query_arg(
     180                        array(
     181                                'mod_req'   => 1,
     182                                'page'      => 'bp-signups',
     183                                'signup_id' => $signup->signup_id,
     184                                'action'    => 'resend',
     185                        ),
     186                        bp_get_admin_url( 'users.php' )
     187                );
     188
     189                $args  = array(
     190                        'tokens' => array(
     191                                'admin.id'                   => $admin_id,
     192                                'manage.url'                 => esc_url_raw( $manage_url ),
     193                                'requesting-user.user_login' => esc_html( $signup->user_login ),
     194                                'unsubscribe'                => esc_url( bp_email_get_unsubscribe_link( $unsubscribe_args ) ),
     195                        ),
     196                );
     197
     198                bp_send_email( 'members-membership-request', (int) $admin_id, $args );
     199        }
     200}
     201add_action( 'bp_members_membership_request_submitted', 'bp_members_membership_requests_notify_site_admins' );
     202
     203/**
     204 * Send a message to the requesting user when his or her
     205 * site membership request has been rejected.
     206 *
     207 * @since 10.0.0
     208 *
     209 * @param array $signup_ids Array of pending IDs to delete.
     210 */
     211function bp_members_membership_requests_send_rejection_mail( $signup_ids ) {
     212        $signups = BP_Signup::get(
     213                array(
     214                        'include' => $signup_ids,
     215                )
     216        );
     217
     218        if ( empty( $signups['signups'] ) ) {
     219                return;
     220        }
     221
     222        foreach ( $signups['signups'] as $signup ) {
     223                if ( ! empty( $signup->user_email ) ) {
     224                        bp_send_email( 'members-membership-request-rejected', $signup->user_email );
     225                }
     226        }
     227}
     228add_action( 'bp_core_signup_before_delete', 'bp_members_membership_requests_send_rejection_mail' );
     229
     230/**
     231 * When a request is approved, activated or deleted,
     232 * delete the associated notifications.
     233 *
     234 * @since 10.0.0
     235 *
     236 * @param array $signup_ids Array of changing signup IDs.
     237 */
     238function bp_members_membership_requests_delete_notifications_on_change( $signup_ids ) {
     239        foreach ( $signup_ids as $signup_id ) {
     240                BP_Notifications_Notification::delete(
     241                        array(
     242                                'item_id'          => $signup_id,
     243                                'component_action' => 'membership_request_submitted',
     244                        )
     245                );
     246        }
     247}
     248add_action( 'bp_core_signup_after_resend',   'bp_members_membership_requests_delete_notifications_on_change' );
     249add_action( 'bp_core_signup_after_activate', 'bp_members_membership_requests_delete_notifications_on_change' );
     250add_action( 'bp_core_signup_after_delete',   'bp_members_membership_requests_delete_notifications_on_change' );
     251
     252/**
     253 * In the Nouveau template pack, when membership requests are required,
     254 * change registration form submit button label to "Submit Request".
     255 *
     256 * @since 10.0.0
     257 *
     258 * @return string $retval the HTML for the request membership link.
     259 */
     260function bp_members_membership_requests_filter_complete_signup_button( $buttons ) {
     261
     262        $buttons['register']['attributes']['value'] = __( 'Submit Request', 'buddypress' );
     263        return $buttons;
     264}
     265add_filter( 'bp_nouveau_get_submit_button', 'bp_members_membership_requests_filter_complete_signup_button' );
     266
     267
     268/**
     269 * Administration: Change certain behavior and labels
     270 * on the WP Admin > Users > Manage Signups screen.
     271 *********************************************************************/
     272
     273/**
     274 * Filter the actions available on the signups list table.
     275 *
     276 * @since 10.0.0
     277 *
     278 * @param array  $actions       Array of actions and corresponding links.
     279 * @param object $signup_object The signup data object.
     280 */
     281function bp_members_membership_requests_filter_signup_row_actions( $actions, $signup_object ) {
     282
     283        // Rename the "email" resend option when membership requests are active.
     284        $email_link = add_query_arg(
     285                array(
     286                        'page'      => 'bp-signups',
     287                        'signup_id' => $signup_object->id,
     288                        'action'    => 'resend',
     289                ),
     290                bp_get_admin_url( 'users.php' )
     291        );
     292
     293        $resend_label = ( 0 === $signup_object->count_sent ) ? __( 'Approve Request', 'buddypress' ) : __( 'Resend Approval', 'buddypress' );
     294
     295        $actions['resend'] = sprintf( '<a href="%1$s">%2$s</a>', esc_url( $email_link ), esc_html( $resend_label ) );
     296
     297        // Add a link to view profile info when membership requests and xprofile are active.
     298        if ( bp_is_active( 'xprofile' ) ) {
     299                $profile_link = add_query_arg(
     300                        array(
     301                                'page'     => 'bp-signups#TB_inline',
     302                                'inlineId' => 'signup-info-modal-' . $signup_object->id,
     303                        ),
     304                        bp_get_admin_url( 'users.php' )
     305                );
     306
     307                $actions['profile'] = sprintf( '<a href="%1$s" class="bp-thickbox">%2$s</a>', esc_url( $profile_link ), esc_html__( 'Profile Info', 'buddypress' ) );
     308        }
     309
     310        return $actions;
     311}
     312add_filter( 'bp_members_ms_signup_row_actions', 'bp_members_membership_requests_filter_signup_row_actions', 10, 2 );
     313
     314/**
     315 * Filter the bulk actions available on the signups list table.
     316 *
     317 * @since 10.0.0
     318 *
     319 * @param array $actions Array of actions and corresponding links.
     320 * @return array         List of actions and corresponding links.
     321 */
     322function bp_members_membership_requests_filter_signup_bulk_actions( $actions ) {
     323        // Rename the "email" resend option when membership requests are active.
     324        $actions['resend'] = esc_html_x( 'Approve', 'Pending signup action', 'buddypress' );
     325        return $actions;
     326}
     327add_filter( 'bp_members_ms_signup_bulk_actions', 'bp_members_membership_requests_filter_signup_bulk_actions' );
     328
     329/**
     330 * Filter the "Last Sent" column header on the pending users table.
     331 *
     332 * @since 10.0.0
     333 *
     334 * @param array $columns Array of columns to display.
     335 * @return array List of columns to display.
     336 */
     337function bp_members_membership_requests_filter_signup_table_date_sent_header( $columns ) {
     338        $columns['date_sent'] = esc_html__( 'Approved', 'buddypress' );
     339        return $columns;
     340}
     341add_filter( 'bp_members_signup_columns', 'bp_members_membership_requests_filter_signup_table_date_sent_header' );
     342add_filter( 'bp_members_ms_signup_columns', 'bp_members_membership_requests_filter_signup_table_date_sent_header' );
     343
     344/**
     345 * Filter the "Last Sent" column message on the pending users table.
     346 *
     347 * @since 10.0.0
     348 *
     349 * @param string      $message "Not yet sent" message.
     350 * @param object|null $signup  Signup object instance.
     351 * @return string              "Not yet approved" message, if needed. Unchanged message otherwise.
     352 */
     353function bp_members_membership_requests_filter_signup_table_unsent_message( $message, $signup ) {
     354        if ( 0 === $signup->count_sent ) {
     355                $message = esc_html__( 'Not yet approved', 'buddypress' );
     356        }
     357
     358        return $message;
     359}
     360add_filter( 'bp_members_signup_date_sent_unsent_message', 'bp_members_membership_requests_filter_signup_table_unsent_message', 10, 2 );
     361add_filter( 'bp_members_ms_signup_date_sent_unsent_message', 'bp_members_membership_requests_filter_signup_table_unsent_message', 10, 2 );
     362
     363/**
     364 * Filter/add "Request Membership" links in the following locations:
     365 * - BP login widget,
     366 * - Sidebar register link,
     367 * - WP Toolbar,
     368 * - WP login form.
     369 *********************************************************************/
     370
     371/**
     372 * Add "Request Membership" link to Widget login form.
     373 *
     374 * @since 10.0.0
     375 *
     376 * @return string $retval the HTML for the request membership link.
     377 */
     378function bp_members_membership_requests_add_link_to_widget_login_form() {
     379        ?>
     380        <span class="bp-login-widget-request-membership-link"><a href="<?php echo esc_url( bp_get_signup_page() ); ?>"><?php esc_html_e( 'Request Membership', 'buddypress' ); ?></a></span>
     381        <?php
     382}
     383add_action( 'bp_login_widget_form', 'bp_members_membership_requests_add_link_to_widget_login_form' );
     384
     385/**
     386 * Filter the "Register" link from `wp_register()` as used in
     387 * `sidebar.php` and the WP Core meta widget.
     388 *
     389 * @since 10.0.0
     390 *
     391 * @param string $link The HTML code for the link to the Registration or Admin page.
     392 * @return string      An empty string or the HTML code for the link to the Membership request page.
     393 */
     394function bp_members_membership_requests_filter_sidebar_register_link( $link ) {
     395        // $link should be an empty string when public registration is disabled.
     396        if ( ! is_user_logged_in() && empty( $link ) ) {
     397                $link = '<a href="' . esc_url( wp_registration_url() ) . '">' . esc_html__( 'Request Membership', 'buddypress' ) . '</a>';
     398        }
     399
     400        return $link;
     401}
     402add_filter( 'register', 'bp_members_membership_requests_filter_sidebar_register_link' );
     403
     404/**
     405 * Add a "Request Membership" link to the WP Toolbar.
     406 * Priority 21 should place it just after the "Log In" link.
     407 *
     408 * @since 10.0.0
     409 *
     410 * @param WP_Admin_Bar $wp_admin_bar WP_Admin_Bar instance, passed by reference
     411 */
     412function bp_members_membership_requests_add_toolbar_link( $wp_admin_bar ) {
     413        if ( is_user_logged_in() ) {
     414                return;
     415        }
     416
     417        $args = array(
     418                'id'    => 'bp-request-membership',
     419                'title' => __( 'Request Membership', 'buddypress' ),
     420                'href'  => wp_registration_url(),
     421                'meta'  => array(
     422                        'class' => 'buddypress-request-membership',
     423                        'title' => __( 'Request Membership', 'buddypress' ),
     424                ),
     425        );
     426
     427        $wp_admin_bar->add_node( $args );
     428}
     429add_action( 'admin_bar_menu', 'bp_members_membership_requests_add_toolbar_link', 21 );
     430
     431/**
     432 * Add a "Request Membership" link to the WP Login form.
     433 *
     434 * @since 10.0.0
     435 *
     436 * @param string $link HTML link to the home URL of the current site.
     437 * @return string      HTML link to the home URL of the current site and the one to request a membership.
     438 */
     439function bp_members_membership_requests_add_link_wp_login( $link ) {
     440        $link_separator = apply_filters( 'login_link_separator', ' | ' );
     441
     442        return $link . $link_separator . '<a href="' . esc_url( wp_registration_url() ) . '">' . esc_html__( 'Request Membership', 'buddypress' ) . '</a>';
     443}
     444add_action( 'login_site_html_link', 'bp_members_membership_requests_add_link_wp_login' );
  • src/bp-members/bp-members-notifications.php

    diff --git src/bp-members/bp-members-notifications.php src/bp-members/bp-members-notifications.php
    index 93cee7b89..08285bcb6 100644
    function members_format_notifications( $action, $item_id, $secondary_item_id, $t 
    6161                                }
    6262                        }
    6363                        break;
     64
     65                case 'membership_request_submitted':
     66                        // $item_id is the id of the signup, not the user ID.
     67                        $signup = new BP_Signup( $item_id );
     68
     69                        // Set up the string and the filter.
     70                        if ( (int) $total_items > 1 ) {
     71                                $link   = bp_get_notifications_permalink();
     72                                $amount = 'multiple';
     73
     74                                $text = sprintf( __( '%d people have requested site membership.', 'buddypress' ), (int) $total_items );
     75                        } else {
     76                                $link   = add_query_arg( array(
     77                                        'mod_req'   => 1,
     78                                        'page'      => 'bp-signups',
     79                                        'signup_id' => $item_id,
     80                                        'action'    => 'resend',
     81                                ), bp_get_admin_url( 'users.php' ) );
     82                                $amount = 'single';
     83
     84                                /* translators: %s: new user name */
     85                                $text = sprintf( __( '%s has requested site membership.', 'buddypress' ),  esc_html( $signup->user_login ) );
     86                        }
     87                        break;
    6488        }
    6589
    6690        // Return either an HTML link or an array, depending on the requested format.
    function bp_members_mark_read_accepted_invitation_notification() { 
    167191                        'is_new' => false,
    168192                ),
    169193                array(
    170                         'user_id' => bp_loggedin_user_id(),
    171                         'item_id' => bp_displayed_user_id(),
     194                        'user_id'          => bp_loggedin_user_id(),
     195                        'item_id'          => bp_displayed_user_id(),
     196                        'component_action' => 'accepted_invitation',
    172197                )
    173198        );
    174199}
    175200add_action( 'bp_screens', 'bp_members_mark_read_accepted_invitation_notification' );
    176201
     202/**
     203 * Mark new membership request notifications as read when user visits Manage BP Signups screen.
     204 *
     205 * @since 10.0.0
     206 */
     207function bp_members_mark_read_submitted_membership_request_notification() {
     208
     209        $signup_screens = array( 'users_page_bp-signups', 'users_page_bp-signups-network' );
     210        if ( ! wp_doing_ajax() && in_array( get_current_screen()->base, $signup_screens, true ) && ! empty( $_GET['mod_req'] ) && ! empty( $_GET['signup_id'] ) ) {
     211                // Mark all notifications about this request as read.
     212                BP_Notifications_Notification::update(
     213                        array(
     214                                'is_new' => false,
     215                        ),
     216                        array(
     217                                'item_id'          => $_GET['signup_id'],
     218                                'component_action' => 'membership_request_submitted',
     219                        )
     220                );
     221        }
     222}
     223add_action( 'admin_footer', 'bp_members_mark_read_submitted_membership_request_notification' );
     224
    177225/**
    178226 * Add Members-related settings to the Settings > Notifications page.
    179227 *
    add_action( 'bp_screens', 'bp_members_mark_read_accepted_invitation_notification 
    181229 */
    182230function members_screen_notification_settings() {
    183231
    184         // Bail early if invitations are not allowed--they are the only members notification so far.
    185         if ( ! bp_get_members_invitations_allowed () ) {
     232        // Bail early if invitations and requests are not allowed--they are the only members notification so far.
     233        if ( ! bp_get_members_invitations_allowed() && ( ! bp_get_membership_requests_required() || ! user_can( bp_displayed_user_id(), 'bp_moderate' ) ) ) {
    186234                return;
    187235        }
    188 
    189         if ( ! $allow_acceptance_emails = bp_get_user_meta( bp_displayed_user_id(), 'notification_members_invitation_accepted', true ) ) {
    190                 $allow_acceptance_emails = 'yes';
    191         }
    192236        ?>
    193237
    194238        <table class="notification-settings" id="members-notification-settings">
    function members_screen_notification_settings() { 
    202246                </thead>
    203247
    204248                <tbody>
    205                         <tr id="members-notification-settings-invitation_accepted">
    206                                 <td></td>
    207                                 <td><?php _ex( 'Someone accepts your membership invitation', 'Member settings on notification settings page', 'buddypress' ) ?></td>
    208                                 <td class="yes"><input type="radio" name="notifications[notification_members_invitation_accepted]" id="notification-members-invitation-accepted-yes" value="yes" <?php checked( $allow_acceptance_emails, 'yes', true ) ?>/><label for="notification-members-invitation-accepted-yes" class="bp-screen-reader-text"><?php
    209                                         /* translators: accessibility text */
    210                                         _e( 'Yes, send email', 'buddypress' );
    211                                 ?></label></td>
    212                                 <td class="no"><input type="radio" name="notifications[notification_members_invitation_accepted]" id="notification-members-invitation-accepted-no" value="no" <?php checked( $allow_acceptance_emails, 'no', true ) ?>/><label for="notification-members-invitation-accepted-no" class="bp-screen-reader-text"><?php
    213                                         /* translators: accessibility text */
    214                                         _e( 'No, do not send email', 'buddypress' );
    215                                 ?></label></td>
    216                         </tr>
    217249
    218250                        <?php
     251                        if ( bp_get_members_invitations_allowed() ) :
     252                                if ( ! $allow_acceptance_emails = bp_get_user_meta( bp_displayed_user_id(), 'notification_members_invitation_accepted', true ) ) {
     253                                        $allow_acceptance_emails = 'yes';
     254                                }
     255                                ?>
     256                                <tr id="members-notification-settings-invitation_accepted">
     257                                        <td></td>
     258                                        <td><?php echo esc_html_x( 'Someone accepts your membership invitation', 'Member settings on notification settings page', 'buddypress' ); ?></td>
     259                                        <td class="yes"><input type="radio" name="notifications[notification_members_invitation_accepted]" id="notification-members-invitation-accepted-yes" value="yes" <?php checked( $allow_acceptance_emails, 'yes', true ) ?>/><label for="notification-members-invitation-accepted-yes" class="bp-screen-reader-text"><?php
     260                                                /* translators: accessibility text */
     261                                                esc_html_e( 'Yes, send email', 'buddypress' );
     262                                        ?></label></td>
     263                                        <td class="no"><input type="radio" name="notifications[notification_members_invitation_accepted]" id="notification-members-invitation-accepted-no" value="no" <?php checked( $allow_acceptance_emails, 'no', true ) ?>/><label for="notification-members-invitation-accepted-no" class="bp-screen-reader-text"><?php
     264                                                /* translators: accessibility text */
     265                                                esc_html_e( 'No, do not send email', 'buddypress' );
     266                                        ?></label></td>
     267                                </tr>
     268                                <?php
     269                        endif;
     270
     271                        if ( bp_get_membership_requests_required() && user_can( bp_displayed_user_id(), 'bp_moderate' ) ) :
     272                                if ( ! $allow_request_emails = bp_get_user_meta( bp_displayed_user_id(), 'notification_members_membership_request', true ) ) {
     273                                        $allow_request_emails = 'yes';
     274                                }
     275                                ?>
     276                                <tr id="members-notification-settings-submitted_membership_request">
     277                                        <td></td>
     278                                        <td><?php echo esc_html_x( 'Someone has requested site membership', 'Member settings on notification settings page', 'buddypress' ) ?></td>
     279                                        <td class="yes"><input type="radio" name="notifications[notification_members_membership_request]" id="notification-members-submitted_membership_request-yes" value="yes" <?php checked( $allow_request_emails, 'yes', true ) ?>/><label for="notification-members-submitted_membership_request-yes" class="bp-screen-reader-text"><?php
     280                                                /* translators: accessibility text */
     281                                                esc_html_e( 'Yes, send email', 'buddypress' );
     282                                        ?></label></td>
     283                                        <td class="no"><input type="radio" name="notifications[notification_members_membership_request]" id="notification-members-submitted_membership_request-no" value="no" <?php checked( $allow_request_emails, 'no', true ) ?>/><label for="notification-members-submitted_membership_request-no" class="bp-screen-reader-text"><?php
     284                                                /* translators: accessibility text */
     285                                                esc_html_e( 'No, do not send email', 'buddypress' );
     286                                        ?></label></td>
     287                                </tr>
     288                                <?php
     289                        endif;
    219290
    220291                        /**
    221292                         * Fires after the last table row on the members notification screen.
  • src/bp-members/bp-members-template.php

    diff --git src/bp-members/bp-members-template.php src/bp-members/bp-members-template.php
    index 10e6ef071..c9616ff9c 100644
    function bp_get_members_invitations_allowed() { 
    29212921        return apply_filters( 'bp_get_members_invitations_allowed', bp_is_active( 'members', 'invitations' ) && (bool) bp_get_option( 'bp-enable-members-invitations' ) );
    29222922}
    29232923
     2924/**
     2925 * Are membership requests required for joining this site?
     2926 *
     2927 * @since 10.0.0
     2928 *
     2929 * @param bool $context "raw" to fetch value from database,
     2930 *                      "site" to take "anyone can register" setting into account.
     2931 * @return bool
     2932 */
     2933function bp_get_membership_requests_required( $context = 'site' ) {
     2934        if ( 'raw' === $context ) {
     2935                $retval = bp_is_active( 'members', 'membership_requests' ) && (bool) bp_get_option( 'bp-enable-membership-requests' );
     2936        } else {
     2937                $retval = bp_is_active( 'members', 'membership_requests' ) && ! bp_get_signup_allowed() && (bool) bp_get_option( 'bp-enable-membership-requests' );
     2938        }
     2939
     2940        /**
     2941         * Filters whether or not prospective members may submit network membership requests.
     2942         *
     2943         * @since 10.0.0
     2944         *
     2945         * @param bool $retval Whether or not membership requests are required.
     2946         * @param bool $retval Whether this is the value stored in the database ('raw')
     2947         *                     or whether the site's "anyone can register" setting is
     2948         *                     being considered ('site' or anything else).
     2949         */
     2950        return apply_filters( 'bp_get_membership_requests_required', $retval, $context );
     2951}
     2952
    29242953/**
    29252954 * Should the system create and allow access
    29262955 * to the Register and Activate pages?
    function bp_get_members_invitations_allowed() { 
    29302959 * @return bool
    29312960 */
    29322961function bp_allow_access_to_registration_pages() {
    2933         $retval = bp_get_signup_allowed() || bp_get_members_invitations_allowed();
     2962        $retval = bp_get_signup_allowed() || bp_get_members_invitations_allowed() || bp_get_membership_requests_required();
    29342963
    29352964        /**
    29362965         * Filters whether or not the system should create and allow access
  • src/bp-members/classes/class-bp-members-admin.php

    diff --git src/bp-members/classes/class-bp-members-admin.php src/bp-members/classes/class-bp-members-admin.php
    index 979847a18..6c94cf20c 100644
    class BP_Members_Admin { 
    514514                // Only show sign-ups where they belong.
    515515                if ( ( ! bp_is_network_activated() && ! is_network_admin() ) || ( is_network_admin() && bp_is_network_activated() ) ) {
    516516
     517                        $signups_menu_label = __( 'Manage Signups',  'buddypress' );
     518
     519                        if ( bp_get_membership_requests_required() ) {
     520                                $signups_menu_label = __( 'Manage Pending Memberships',  'buddypress' );
     521                        }
     522
    517523                        // Manage signups.
    518524                        $hooks['signups'] = $this->signups_page = add_users_page(
    519                                 __( 'Manage Signups',  'buddypress' ),
    520                                 __( 'Manage Signups',  'buddypress' ),
     525                                $signups_menu_label,
     526                                $signups_menu_label,
    521527                                $this->capability,
    522528                                'bp-signups',
    523529                                array( $this, 'signups_admin' )
    class BP_Members_Admin { 
    16871693                 */
    16881694                $allowed_actions = apply_filters( 'bp_signups_admin_allowed_actions', array( 'do_delete', 'do_activate', 'do_resend' ) );
    16891695
    1690                 // Prepare the display of the Community Profile screen.
     1696                // Prepare the display of the Signups screen.
    16911697                if ( ! in_array( $doaction, $allowed_actions ) || ( -1 == $doaction ) ) {
    16921698
    16931699                        if ( is_network_admin() ) {
    class BP_Members_Admin { 
    17091715                                '<p>' . __( 'Using the search form, you can find pending accounts more easily. The Username and Email fields will be included in the search.', 'buddypress' ) . '</p>'
    17101716                        ) );
    17111717
     1718                        $signup_help_content = '<p>' . esc_html__( 'Hovering over a row in the pending accounts list will display action links that allow you to manage pending accounts. You can perform the following actions:', 'buddypress' ) . '</p>';
     1719
     1720                        if ( bp_get_membership_requests_required() ) {
     1721                                $signup_help_content .= '<ul><li>' . esc_html__( '"Activate" will activate the user immediately without requiring that they validate their email.', 'buddypress' ) .'</li>' .
     1722                                        '<li>' . esc_html__( '"Approve Request" or "Resend Approval" takes you to the confirmation screen before being able to send the activation link to the desired pending request. You can only send the activation email once per day.', 'buddypress' ) . '</li>';
     1723
     1724                                if ( bp_is_active( 'xprofile' ) ) {
     1725                                        $signup_help_content .= '<li>' . esc_html__( '"Profile Info" will display extended profile information for the request.', 'buddypress' ) . '</li>';
     1726                                }
     1727
     1728                                $signup_help_content .= '<li>' . esc_html__( '"Delete" allows you to delete a pending account from your site. You will be asked to confirm this deletion.', 'buddypress' ) . '</li></ul>';
     1729                        } else {
     1730                                $signup_help_content .= '<ul><li>' . esc_html__( '"Email" takes you to the confirmation screen before being able to send the activation link to the desired pending account. You can only send the activation email once per day.', 'buddypress' ) . '</li>' .
     1731                                        '<li>' . __( '"Delete" allows you to delete a pending account from your site. You will be asked to confirm this deletion.', 'buddypress' ) . '</li></ul>';
     1732                        }
     1733
     1734                        $signup_help_content .= '<p>' . esc_html__( 'By clicking on a Username you will be able to activate a pending account from the confirmation screen.', 'buddypress' ) . '</p>' .
     1735                                '<p>' . __( 'Bulk actions allow you to perform these 3 actions for the selected rows.', 'buddypress' ) . '</p>';
     1736
    17121737                        get_current_screen()->add_help_tab( array(
    17131738                                'id'      => 'bp-signups-actions',
    17141739                                'title'   => __( 'Actions', 'buddypress' ),
    1715                                 'content' =>
    1716                                 '<p>' . __( 'Hovering over a row in the pending accounts list will display action links that allow you to manage pending accounts. You can perform the following actions:', 'buddypress' ) . '</p>' .
    1717                                 '<ul><li>' . __( '"Email" takes you to the confirmation screen before being able to send the activation link to the desired pending account. You can only send the activation email once per day.', 'buddypress' ) . '</li>' .
    1718                                 '<li>' . __( '"Delete" allows you to delete a pending account from your site. You will be asked to confirm this deletion.', 'buddypress' ) . '</li></ul>' .
    1719                                 '<p>' . __( 'By clicking on a Username you will be able to activate a pending account from the confirmation screen.', 'buddypress' ) . '</p>' .
    1720                                 '<p>' . __( 'Bulk actions allow you to perform these 3 actions for the selected rows.', 'buddypress' ) . '</p>'
     1740                                'content' => $signup_help_content
    17211741                        ) );
    17221742
    17231743                        // Help panel - sidebar links.
    17241744                        get_current_screen()->set_help_sidebar(
    1725                                 '<p><strong>' . __( 'For more information:', 'buddypress' ) . '</strong></p>' .
     1745                                '<p><strong>' . esc_html__( 'For more information:', 'buddypress' ) . '</strong></p>' .
    17261746                                '<p>' . __( '<a href="https://buddypress.org/support/">Support Forums</a>', 'buddypress' ) . '</p>'
    17271747                        );
    17281748
    class BP_Members_Admin { 
    17361756                                'heading_list'       => __( 'Pending users list', 'buddypress' ),
    17371757                        ) );
    17381758
     1759                        // Use thickbox to display the extended profile information.
     1760                        if ( bp_is_active( 'xprofile' ) ) {
     1761                                wp_enqueue_style( 'thickbox' );
     1762                                wp_enqueue_script(
     1763                                        'bp-signup-preview',
     1764                                        $this->js_url . 'signup-preview' . bp_core_get_minified_asset_suffix() . '.js',
     1765                                        array( 'bp-thickbox', 'jquery' ),
     1766                                        bp_get_version(),
     1767                                        true
     1768                                );
     1769                                wp_localize_script(
     1770                                        'bp-signup-preview',
     1771                                        'bpSignupPreview',
     1772                                        array(
     1773                                                'modalLabel' => __( 'Profile info preview', 'buddypress' ),
     1774                                        )
     1775                                );
     1776                        }
     1777
    17391778                } else {
    17401779                        if ( ! empty( $_REQUEST['signup_ids' ] ) ) {
    17411780                                $signups = wp_parse_id_list( $_REQUEST['signup_ids' ] );
    class BP_Members_Admin { 
    22042243                ) );
    22052244
    22062245                $signups    = $signups_query['signups'];
    2207                 $signup_ids = wp_list_pluck( $signups, 'signup_id' );
     2246                $signup_ids = wp_list_pluck( $signups, 'id' );
    22082247
    22092248                // Set up strings.
    22102249                switch ( $action ) {
    class BP_Members_Admin { 
    22272266                                break;
    22282267
    22292268                        case 'resend' :
    2230                                 $header_text = __( 'Resend Activation Emails', 'buddypress' );
    2231                                 if ( 1 == count( $signup_ids ) ) {
    2232                                         $helper_text = __( 'You are about to resend an activation email to the following account:', 'buddypress' );
     2269
     2270                                if ( bp_get_membership_requests_required() ) {
     2271                                        $header_text = __( 'Approve Membership Requests', 'buddypress' );
     2272                                        if ( 1 === count( $signup_ids ) ) {
     2273                                                $helper_text = __( 'You are about to send an approval email to the following user:', 'buddypress' );
     2274                                        } else {
     2275                                                $helper_text = __( 'You are about to send approval emails to the following users:', 'buddypress' );
     2276                                        }
    22332277                                } else {
    2234                                         $helper_text = __( 'You are about to resend an activation email to the following accounts:', 'buddypress' );
     2278                                        $header_text = __( 'Resend Activation Emails', 'buddypress' );
     2279                                        if ( 1 === count( $signup_ids ) ) {
     2280                                                $helper_text = __( 'You are about to resend an activation email to the following account:', 'buddypress' );
     2281                                        } else {
     2282                                                $helper_text = __( 'You are about to resend an activation email to the following accounts:', 'buddypress' );
     2283                                        }
    22352284                                }
    22362285                                break;
     2286
    22372287                }
    22382288
    22392289                // These arguments are added to all URLs.
    class BP_Members_Admin { 
    22622312
    22632313                // Prefetch registration field data.
    22642314                $fdata = array();
    2265                 if ( 'activate' === $action && bp_is_active( 'xprofile' ) ) {
     2315                if ( bp_is_active( 'xprofile' ) && ( 'activate' == $action || ( 'resend' == $action && bp_get_membership_requests_required() ) ) ) {
    22662316                        $field_groups = bp_xprofile_get_groups( array(
    22672317                                'exclude_fields'    => 1,
    22682318                                'update_meta_cache' => false,
    class BP_Members_Admin { 
    22862336
    22872337                        <ol class="bp-signups-list">
    22882338                        <?php foreach ( $signups as $signup ) :
    2289                                 $last_notified = mysql2date( 'Y/m/d g:i:s a', $signup->date_sent );
     2339                                if ( $signup->count_sent > 0 ) {
     2340                                        $last_notified = mysql2date( 'Y/m/d g:i:s a', $signup->date_sent );
     2341                                } else {
     2342                                        $last_notified = __( 'Not yet notified', 'buddypress' );
     2343                                }
    22902344                                $profile_field_ids = array();
    22912345
    22922346                                // Get all xprofile field IDs except field 1.
    class BP_Members_Admin { 
    22982352                                <li>
    22992353                                        <strong><?php echo esc_html( $signup->user_login ) ?></strong>
    23002354
    2301                                         <?php if ( 'activate' == $action ) : ?>
     2355                                        <?php if ( 'activate' == $action || ( 'resend' == $action && bp_get_membership_requests_required() ) ) : ?>
    23022356                                                <table class="wp-list-table widefat fixed striped">
    23032357                                                        <tbody>
    23042358                                                                <tr>
    class BP_Members_Admin { 
    23162370                                                                                $field_value = isset( $signup->meta[ "field_{$pid}" ] ) ? $signup->meta[ "field_{$pid}" ] : ''; ?>
    23172371                                                                                <tr>
    23182372                                                                                        <td class="column-fields"><?php echo esc_html( $fdata[ $pid ] ); ?></td>
    2319                                                                                         <td><?php echo $this->format_xprofile_field_for_display( $field_value ); ?></td>
     2373                                                                                        <td><?php echo bp_members_admin_format_xprofile_field_for_display( $field_value ); ?></td>
    23202374                                                                                </tr>
    23212375
    23222376                                                                        <?php endforeach;  ?>
    class BP_Members_Admin { 
    26222676         * Operates recursively on arrays, which are then imploded with commas.
    26232677         *
    26242678         * @since 2.8.0
     2679         * @deprecated 10.0.0
    26252680         *
    26262681         * @param string|array $value Field value.
    26272682         * @return string
    26282683         */
    26292684        protected function format_xprofile_field_for_display( $value ) {
    2630                 if ( is_array( $value ) ) {
    2631                         $value = array_map( array( $this, 'format_xprofile_field_for_display' ), $value );
    2632                         $value = implode( ', ', $value );
    2633                 } else {
    2634                         $value = stripslashes( $value );
    2635                         $value = esc_html( $value );
    2636                 }
     2685                _deprecated_function( __METHOD__, '10.0.0', 'bp_members_admin_format_xprofile_field_for_display' );
    26372686
    2638                 return $value;
     2687                return bp_members_admin_format_xprofile_field_for_display( $value );
    26392688        }
    26402689
    26412690        /**
  • src/bp-members/classes/class-bp-members-component.php

    diff --git src/bp-members/classes/class-bp-members-component.php src/bp-members/classes/class-bp-members-component.php
    index 7bca5d8bb..17c82b95f 100644
    class BP_Members_Component extends BP_Component { 
    4040                        array(
    4141                                'adminbar_myaccount_order' => 20,
    4242                                'search_query_arg'         => 'members_search',
    43                                 'features'                 => array( 'invitations' )
     43                                'features'                 => array( 'invitations', 'membership_requests' ),
    4444                        )
    4545                );
    4646        }
    class BP_Members_Component extends BP_Component { 
    7474                        $includes[] = 'activity';
    7575                }
    7676
     77                /**
     78                 * Duplicate bp_get_membership_requests_required() and
     79                 * bp_get_signup_allowed() logic here,
     80                 * because those functions are not available yet.
     81                 * The `bp_get_signup_allowed` filter is documented in
     82                 * bp-members/bp-members-template.php.
     83                 */
     84                $signup_allowed = apply_filters( 'bp_get_signup_allowed', (bool) bp_get_option( 'users_can_register' ) );
     85                $membership_requests_enabled = (bool) bp_get_option( 'bp-enable-membership-requests' );
     86                if ( bp_is_active( 'members', 'membership_requests' ) && ! $signup_allowed && $membership_requests_enabled ) {
     87                        $includes[] = 'membership-requests';
     88                }
     89
    7790                // Include these only if in admin.
    7891                if ( is_admin() ) {
    7992                        $includes[] = 'admin';
  • src/bp-members/classes/class-bp-members-list-table.php

    diff --git src/bp-members/classes/class-bp-members-list-table.php src/bp-members/classes/class-bp-members-list-table.php
    index 8e7733b47..0dcf21d7c 100644
    class BP_Members_List_Table extends WP_Users_List_Table { 
    2626         */
    2727        public $signup_counts = 0;
    2828
     29        /**
     30         * Signup profile fields.
     31         *
     32         * @since 10.0.0
     33         *
     34         * @var array
     35         */
     36        public $signup_field_labels = array();
     37
    2938        /**
    3039         * Constructor.
    3140         *
    class BP_Members_List_Table extends WP_Users_List_Table { 
    138147         */
    139148        public function get_columns() {
    140149
    141                 /**
    142                  * Filters the single site Members signup columns.
    143                  *
    144                  * @since 2.0.0
    145                  *
    146                  * @param array $value Array of columns to display.
    147                  */
    148                 return apply_filters( 'bp_members_signup_columns', array(
     150                $columns = array(
    149151                        'cb'         => '<input type="checkbox" />',
    150152                        'username'   => __( 'Username',    'buddypress' ),
    151153                        'name'       => __( 'Name',        'buddypress' ),
    class BP_Members_List_Table extends WP_Users_List_Table { 
    153155                        'registered' => __( 'Registered',  'buddypress' ),
    154156                        'date_sent'  => __( 'Last Sent',   'buddypress' ),
    155157                        'count_sent' => __( 'Emails Sent', 'buddypress' )
    156                 ) );
     158                );
     159
     160                /**
     161                 * Filters the single site Members signup columns.
     162                 *
     163                 * @since 2.0.0
     164                 *
     165                 * @param array $value Array of columns to display.
     166                 */
     167                return apply_filters( 'bp_members_signup_columns', $columns );
    157168        }
    158169
    159170        /**
    class BP_Members_List_Table extends WP_Users_List_Table { 
    171182                        $actions['delete'] = __( 'Delete', 'buddypress' );
    172183                }
    173184
    174                 return $actions;
     185                /**
     186                 * Filters the bulk actions for signups.
     187                 *
     188                 * @since 10.0.0
     189                 *
     190                 * @param array $actions Array of actions and corresponding labels.
     191                 */
     192                return apply_filters( 'bp_members_ms_signup_bulk_actions', $actions );
    175193        }
    176194
    177195        /**
    class BP_Members_List_Table extends WP_Users_List_Table { 
    183201         */
    184202        public function no_items() {
    185203
    186                 if ( bp_get_signup_allowed() ) {
     204                if ( bp_get_signup_allowed() || bp_get_membership_requests_required() ) {
    187205                        esc_html_e( 'No pending accounts found.', 'buddypress' );
    188206                } else {
    189207                        $link = false;
    class BP_Members_List_Table extends WP_Users_List_Table { 
    342360         */
    343361        public function column_name( $signup_object = null ) {
    344362                echo esc_html( $signup_object->user_name );
     363
     364                // Insert the extended profile modal content required by thickbox.
     365                if ( ! bp_is_active( 'xprofile' ) ) {
     366                        return;
     367                }
     368
     369                $profile_field_ids = array();
     370
     371                // Fetch registration field data once only.
     372                if ( ! $this->signup_field_labels ) {
     373                        $field_groups = bp_xprofile_get_groups(
     374                                array(
     375                                        'fetch_fields'       => true,
     376                                        'signup_fields_only' => true,
     377                                )
     378                        );
     379
     380                        foreach( $field_groups as $field_group ) {
     381                                foreach( $field_group->fields as $field ) {
     382                                        $this->signup_field_labels[ $field->id ] = $field->name;
     383                                }
     384                        }
     385                }
     386
     387                bp_members_admin_preview_signup_profile_info( $this->signup_field_labels, $signup_object );
    345388        }
    346389
    347390        /**
    class BP_Members_List_Table extends WP_Users_List_Table { 
    363406         * @param object|null $signup_object The signup data object.
    364407         */
    365408        public function column_registered( $signup_object = null ) {
    366                 echo mysql2date( 'Y/m/d', $signup_object->registered );
     409                echo mysql2date( 'Y/m/d g:i:s a', $signup_object->registered );
    367410        }
    368411
    369412        /**
    class BP_Members_List_Table extends WP_Users_List_Table { 
    374417         * @param object|null $signup_object The signup data object.
    375418         */
    376419        public function column_date_sent( $signup_object = null ) {
    377                 echo mysql2date( 'Y/m/d', $signup_object->date_sent );
     420                if ( $signup_object->count_sent > 0 ) {
     421                        echo mysql2date( 'Y/m/d g:i:s a', $signup_object->date_sent );
     422                } else {
     423                        $message = __( 'Not yet notified', 'buddypress' );
     424
     425                        /**
     426                         * Filters the "not yet sent" message for "Last Sent"
     427                         * column in Manage Signups list table.
     428                         *
     429                         * @since 10.0.0
     430                         *
     431                         * @param string      $message       "Not yet sent" message.
     432                         * @param object|null $signup_object Signup object instance.
     433                         */
     434                        $message = apply_filters( 'bp_members_signup_date_sent_unsent_message', $message, $signup_object );
     435
     436                        echo esc_html( $message );
     437                }
    378438        }
    379439
    380440        /**
    381          * Display number of time an activation email has been sent.
     441         * Display number of times an activation email has been sent.
    382442         *
    383443         * @since 2.0.0
    384444         *
  • src/bp-members/classes/class-bp-members-ms-list-table.php

    diff --git src/bp-members/classes/class-bp-members-ms-list-table.php src/bp-members/classes/class-bp-members-ms-list-table.php
    index b8e86f906..27015f220 100644
    class BP_Members_MS_List_Table extends WP_MS_Users_List_Table { 
    2626         */
    2727        public $signup_counts = 0;
    2828
     29        /**
     30         * Signup profile fields.
     31         *
     32         * @since 10.0.0
     33         *
     34         * @var array
     35         */
     36        public $signup_field_labels = array();
     37
    2938        /**
    3039         * Constructor.
    3140         *
    class BP_Members_MS_List_Table extends WP_MS_Users_List_Table { 
    114123
    115124                // Reset the screen id.
    116125                $this->screen->id = $reset_screen_id;
     126
     127                // Use thickbox to display the extended profile information.
     128                if ( bp_is_active( 'xprofile' ) ) {
     129                        add_thickbox();
     130                }
    117131        }
    118132
    119133        /**
    class BP_Members_MS_List_Table extends WP_MS_Users_List_Table { 
    125139         */
    126140        public function get_columns() {
    127141
    128                 /**
    129                  * Filters the multisite Members signup columns.
    130                  *
    131                  * @since 2.0.0
    132                  *
    133                  * @param array $value Array of columns to display.
    134                  */
    135                 return apply_filters( 'bp_members_ms_signup_columns', array(
     142                $columns = array(
    136143                        'cb'         => '<input type="checkbox" />',
    137144                        'username'   => __( 'Username',    'buddypress' ),
    138145                        'name'       => __( 'Name',        'buddypress' ),
    class BP_Members_MS_List_Table extends WP_MS_Users_List_Table { 
    140147                        'registered' => __( 'Registered',  'buddypress' ),
    141148                        'date_sent'  => __( 'Last Sent',   'buddypress' ),
    142149                        'count_sent' => __( 'Emails Sent', 'buddypress' )
    143                 ) );
     150                );
     151
     152                /**
     153                 * Filters the multisite Members signup columns.
     154                 *
     155                 * @since 2.0.0
     156                 *
     157                 * @param array $value Array of columns to display.
     158                 */
     159                return apply_filters( 'bp_members_ms_signup_columns', $columns );
    144160        }
    145161
    146162        /**
    class BP_Members_MS_List_Table extends WP_MS_Users_List_Table { 
    158174                        $actions['delete'] = __( 'Delete', 'buddypress' );
    159175                }
    160176
    161                 return $actions;
     177                /**
     178                 * Filters the bulk actions for signups.
     179                 *
     180                 * @since 10.0.0
     181                 *
     182                 * @param array $actions Array of actions and corresponding labels.
     183                 */
     184                return apply_filters( 'bp_members_ms_signup_bulk_actions', $actions );
    162185        }
    163186
    164187        /**
    class BP_Members_MS_List_Table extends WP_MS_Users_List_Table { 
    169192         * @since 2.0.0
    170193         */
    171194        public function no_items() {
    172                 if ( bp_get_signup_allowed() ) {
     195                if ( bp_get_signup_allowed() || bp_get_membership_requests_required() ) {
    173196                        esc_html_e( 'No pending accounts found.', 'buddypress' );
    174197                } else {
    175198                        $link = false;
    class BP_Members_MS_List_Table extends WP_MS_Users_List_Table { 
    328351         */
    329352        public function column_name( $signup_object = null ) {
    330353                echo esc_html( $signup_object->user_name );
     354
     355                // Insert the extended profile modal content required by thickbox.
     356                if ( ! bp_is_active( 'xprofile' ) ) {
     357                        return;
     358                }
     359
     360                $profile_field_ids = array();
     361
     362                // Fetch registration field data once only.
     363                if ( ! $this->signup_field_labels ) {
     364                        $field_groups = bp_xprofile_get_groups(
     365                                array(
     366                                        'fetch_fields'       => true,
     367                                        'signup_fields_only' => true,
     368                                )
     369                        );
     370
     371                        foreach( $field_groups as $field_group ) {
     372                                foreach( $field_group->fields as $field ) {
     373                                        $this->signup_field_labels[ $field->id ] = $field->name;
     374                                }
     375                        }
     376                }
     377
     378                bp_members_admin_preview_signup_profile_info( $this->signup_field_labels, $signup_object );
    331379        }
    332380
    333381        /**
    class BP_Members_MS_List_Table extends WP_MS_Users_List_Table { 
    376424                        $date = 'Y/m/d \<\b\r \/\> g:i:s a';
    377425                }
    378426
    379                 echo mysql2date( $date, $signup_object->date_sent );
     427                if ( $signup_object->count_sent > 0 ) {
     428                        echo mysql2date( $date, $signup_object->date_sent );
     429                } else {
     430                        $message = __( 'Not yet notified', 'buddypress' );
     431
     432                        /**
     433                         * Filters the "not yet sent" message for "Last Sent"
     434                         * column in Manage Signups list table.
     435                         *
     436                         * @since 10.0.0
     437                         *
     438                         * @param string      $message       "Not yet sent" message.
     439                         * @param object|null $signup_object Signup object instance.
     440                         */
     441                        $message = apply_filters( 'bp_members_ms_signup_date_sent_unsent_message', $message, $signup_object );
     442
     443                        echo esc_html( $message );
     444                }
    380445        }
    381446
    382447        /**
  • src/bp-members/classes/class-bp-registration-theme-compat.php

    diff --git src/bp-members/classes/class-bp-registration-theme-compat.php src/bp-members/classes/class-bp-registration-theme-compat.php
    index 6a942a58b..f4e3e6855 100644
    class BP_Registration_Theme_Compat { 
    9797        public function dummy_post() {
    9898                // Registration page.
    9999                if ( bp_is_register_page() ) {
    100                         $title = __( 'Create an Account', 'buddypress' );
     100                        if ( bp_get_membership_requests_required() ) {
     101                                $title = __( 'Request Membership', 'buddypress' );
     102                        } else {
     103                                $title = __( 'Create an Account', 'buddypress' );
     104                        }
    101105
    102106                        if ( 'completed-confirmation' == bp_get_current_signup_step() ) {
    103                                 $title = __( 'Check Your Email To Activate Your Account!', 'buddypress' );
     107                                if ( bp_get_membership_requests_required() ) {
     108                                        $title = __( 'Your Membership Request has been submitted.', 'buddypress' );
     109                                } else {
     110                                        $title = __( 'Check Your Email To Activate Your Account!', 'buddypress' );
     111                                }
    104112                        }
    105113
    106114                // Activation page.
  • src/bp-members/classes/class-bp-signup.php

    diff --git src/bp-members/classes/class-bp-signup.php src/bp-members/classes/class-bp-signup.php
    index 9de2af9fe..76b2022d0 100644
    class BP_Signup { 
    214214                        $this->date_sent = $signup->registered;
    215215                }
    216216
     217                // How many times has the activation email been sent?
     218                if ( isset( $this->meta['count_sent'] ) ) {
     219                        $this->count_sent = absint( $this->meta['count_sent'] );
     220                } else {
     221                        /**
     222                         * Meta will not be set if this is a pre-10.0 signup.
     223                         * In this case, we assume that the count is 1.
     224                         */
     225                        $this->count_sent = 1;
     226                }
     227
    217228                /**
    218229                 * Calculate a diff between now & last time
    219230                 * an activation link has been resent.
    class BP_Signup { 
    226237                 * Set a boolean to track whether an activation link
    227238                 * was sent in the last day.
    228239                 */
    229                 $this->recently_sent = ( $diff < 1 * DAY_IN_SECONDS );
     240                $this->recently_sent = $this->count_sent && ( $diff < 1 * DAY_IN_SECONDS );
    230241
    231                 // How many times has the activation email been sent?
    232                 if ( isset( $this->meta['count_sent'] ) ) {
    233                         $this->count_sent = absint( $this->meta['count_sent'] );
    234                 } else {
    235                         $this->count_sent = 0;
    236                 }
    237242        }
    238243
    239244        /** Static Methods *******************************************************/
  • src/bp-members/screens/register.php

    diff --git src/bp-members/screens/register.php src/bp-members/screens/register.php
    index 77929c442..2f3f1da1b 100644
    function bp_core_screen_signup() { 
    6161                }
    6262        }
    6363
    64         if ( ! bp_get_signup_allowed() && ! $active_invite ) {
     64        $requests_enabled = bp_get_membership_requests_required();
     65
     66        if ( ! bp_get_signup_allowed() && ! $active_invite && ! $requests_enabled ) {
    6567                $bp->signup->step = 'registration-disabled';
    6668                // If the signup page is submitted, validate and save.
    6769        } elseif ( isset( $_POST['signup_submit'] ) && bp_verify_nonce_request( 'bp_new_signup' ) ) {
    function bp_core_screen_signup() { 
    197199                        // No errors! Let's register those deets.
    198200                        $active_signup = bp_core_get_root_option( 'registration' );
    199201
    200                         if ( 'none' != $active_signup ) {
     202                        if ( 'none' != $active_signup || $requests_enabled ) {
    201203
    202204                                // Make sure the extended profiles module is enabled.
    203205                                if ( bp_is_active( 'xprofile' ) ) {
  • src/bp-templates/bp-legacy/buddypress/members/register.php

    diff --git src/bp-templates/bp-legacy/buddypress/members/register.php src/bp-templates/bp-legacy/buddypress/members/register.php
    index d263470f2..dd6365b45 100644
     
    351351                         *
    352352                         * @since 1.1.0
    353353                         */
    354                         do_action( 'bp_before_registration_submit_buttons' ); ?>
     354                        do_action( 'bp_before_registration_submit_buttons' );
     355
     356                        if ( bp_get_membership_requests_required() ) {
     357                                $button_text = __( 'Submit Request', 'buddypress' );
     358                        } else {
     359                                $button_text = __( 'Complete Sign Up', 'buddypress' );
     360                        }
     361                        ?>
    355362
    356363                        <div class="submit">
    357                                 <input type="submit" name="signup_submit" id="signup_submit" value="<?php esc_attr_e( 'Complete Sign Up', 'buddypress' ); ?>" />
     364                                <input type="submit" name="signup_submit" id="signup_submit" value="<?php echo esc_attr( $button_text ); ?>" />
    358365                        </div>
    359366
    360367                        <?php
     
    390397                        do_action( 'bp_before_registration_confirmed' ); ?>
    391398
    392399                        <div id="template-notices" role="alert" aria-atomic="true">
    393                                 <?php if ( bp_registration_needs_activation() ) : ?>
     400                                <?php if ( bp_get_membership_requests_required() ) : ?>
     401                                        <p><?php _e( 'You have successfully submitted your membership request! Our site moderators will review your submission and send you an activation email if your request is approved.', 'buddypress' ); ?></p>
     402                                <?php elseif ( bp_registration_needs_activation() ) : ?>
    394403                                        <p><?php _e( 'You have successfully created your account! To begin using this site you will need to activate your account via the email we have just sent to your address.', 'buddypress' ); ?></p>
    395404                                <?php else : ?>
    396405                                        <p><?php _e( 'You have successfully created your account! Please log in using the username and password you have just created.', 'buddypress' ); ?></p>
  • src/bp-templates/bp-nouveau/includes/functions.php

    diff --git src/bp-templates/bp-nouveau/includes/functions.php src/bp-templates/bp-nouveau/includes/functions.php
    index 8f36ac097..3e6bb22b1 100644
    function bp_nouveau_get_user_feedback( $feedback_id = '' ) { 
    11221122        /*
    11231123         * Adjust some messages to the context.
    11241124         */
    1125         if ( 'completed-confirmation' === $feedback_id && bp_registration_needs_activation() ) {
     1125        if ( 'completed-confirmation' === $feedback_id && bp_get_membership_requests_required() ) {
     1126                $feedback_messages['completed-confirmation']['message'] = __( 'You have successfully submitted your membership request! Our site moderators will review your submission and send you an activation email if your request is approved.', 'buddypress' );
     1127        } elseif ( 'completed-confirmation' === $feedback_id && bp_registration_needs_activation() ) {
    11261128                $feedback_messages['completed-confirmation']['message'] = __( 'You have successfully created your account! To begin using this site you will need to activate your account via the email we have just sent to your address.', 'buddypress' );
    11271129        } elseif ( 'member-notifications-none' === $feedback_id ) {
    11281130                $is_myprofile = bp_is_my_profile();
  • src/class-buddypress.php

    diff --git src/class-buddypress.php src/class-buddypress.php
    index 981ae06a1..41be07434 100644
    class BuddyPress { 
    350350                /** Versions */
    351351
    352352                $this->version    = '10.0.0-alpha';
    353                 $this->db_version = 12850;
     353                $this->db_version = 13150;
    354354
    355355                /** Loading */
    356356