Skip to:
Content

BuddyPress.org

Ticket #8139: 8139.05.refreshed.patch

File 8139.05.refreshed.patch, 163.3 KB (added by imath, 3 months ago)
  • src/bp-core/admin/bp-core-admin-functions.php

    diff --git src/bp-core/admin/bp-core-admin-functions.php src/bp-core/admin/bp-core-admin-functions.php
    index 7749dd449..3096ba338 100644
    function bp_core_modify_admin_menu_highlight() { 
    8686        }
    8787
    8888        // Keep the BuddyPress tools menu highlighted.
    89         if ( 'bp-optouts' === $plugin_page ) {
     89        if ( 'bp-optouts' === $plugin_page || 'bp-members-invitations' === $plugin_page ) {
    9090                $submenu_file = 'bp-tools';
    9191        }
    9292}
    function bp_core_activation_notice() { 
    284284
    285285        // Activate and Register are special cases. They are not components but they need WP pages.
    286286        // If user registration is disabled, we can skip this step.
    287         if ( bp_get_signup_allowed() ) {
     287        if ( bp_get_signup_allowed() || bp_get_members_invitations_allowed() ) {
    288288                $wp_page_components[] = array(
    289289                        'id'   => 'activate',
    290290                        'name' => __( 'Activate', 'buddypress' ),
    function bp_core_get_admin_tabs( $active_tab = '', $context = 'settings' ) { 
    479479                                'name' => __( 'Repair', 'buddypress' ),
    480480                        ),
    481481                        '1' => array(
     482                                'href' => bp_get_admin_url( add_query_arg( array( 'page' => 'bp-members-invitations' ), $tools_page ) ),
     483                                'name' => __( 'Manage Invitations', 'buddypress' ),
     484                        ),
     485                        '2' => array(
    482486                                'href' => bp_get_admin_url( add_query_arg( array( 'page' => 'bp-optouts' ), $tools_page ) ),
    483487                                'name' => __( 'Manage Opt-outs', 'buddypress' ),
    484488                        ),
  • 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 c09f809ca..b37b914ff 100644
    function bp_admin_setting_callback_cover_image_uploads() { 
    182182<?php
    183183}
    184184
     185/**
     186 * Allow members to invite non-members to the network.
     187 *
     188 * @since 8.0.0
     189 */
     190function bp_admin_setting_callback_members_invitations() {
     191?>
     192        <input id="bp-enable-members-invitations" name="bp-enable-members-invitations" type="checkbox" value="1" <?php checked( bp_get_members_invitations_allowed() ); ?> />
     193        <label for="bp-enable-members-invitations"><?php _e( 'Allow registered members to invite people to join this network', 'buddypress' ); ?></label>
     194        <?php if ( ! bp_get_signup_allowed() ) : ?>
     195                <p class="description"><?php _e( 'Public registration is currently disabled. However, invitees will still be able to register if network invitations are enabled.', 'buddypress' ); ?></p>
     196        <?php endif; ?>
     197        <?php
     198        /**
     199         * Fires after the output of the invitations settings section.
     200         *
     201         * @since 8.0.0
     202         */
     203        do_action( 'bp_admin_settings_after_members_invitations' );
     204}
     205
    185206/** XProfile ******************************************************************/
    186207
    187208/**
  • src/bp-core/admin/bp-core-admin-slugs.php

    diff --git src/bp-core/admin/bp-core-admin-slugs.php src/bp-core/admin/bp-core-admin-slugs.php
    index 8fb9b9a40..52ef6e36c 100644
    function bp_core_admin_slugs_options() { 
    187187
    188188                <h3><?php _e( 'Registration', 'buddypress' ); ?></h3>
    189189
    190                 <?php if ( bp_get_signup_allowed() ) : ?>
     190                <?php if ( bp_get_signup_allowed() || bp_get_members_invitations_allowed() ) : ?>
    191191                        <p><?php _e( 'Associate WordPress Pages with the following BuddyPress Registration pages.', 'buddypress' ); ?></p>
    192192                <?php else : ?>
    193193                        <?php if ( is_multisite() ) : ?>
    function bp_core_admin_slugs_options() { 
    210210                <table class="form-table">
    211211                        <tbody>
    212212
    213                                 <?php if ( bp_get_signup_allowed() ) : foreach ( $static_pages as $name => $label ) : ?>
     213                                <?php if ( bp_get_signup_allowed() || bp_get_members_invitations_allowed() ) : foreach ( $static_pages as $name => $label ) : ?>
    214214
    215215                                        <tr valign="top">
    216216                                                <th scope="row">
  • src/bp-core/admin/bp-core-admin-tools.php

    diff --git src/bp-core/admin/bp-core-admin-tools.php src/bp-core/admin/bp-core-admin-tools.php
    index f93549295..bd2a2d3f8 100644
    function bp_core_admin_available_tools_intro() { 
    557557                                );
    558558                                ?>
    559559                        </dd>
     560
     561                        <dt><?php esc_html_e( 'Manage Invitations', 'buddypress' ) ?></dt>
     562                        <dd>
     563                                <?php esc_html_e( 'When enabled, BuddyPress allows your users to invite nonmembers to join your site.', 'buddypress' ); ?>
     564                                <?php
     565                                $url = add_query_arg( 'page', 'bp-members-invitations', bp_get_admin_url( $page ) );
     566                                printf(
     567                                        /* translators: %s: the link to the BuddyPress Invitations management tool screen */
     568                                        esc_html_x( 'Visit %s to manage your site&rsquo;s invitations.', 'buddypress invitations tool intro', 'buddypress' ),
     569                                        '<a href="' . esc_url( $url ) . '">' . esc_html__( 'Invitations', 'buddypress' ) . '</a>'
     570                                );
     571                                ?>
     572                        </dd>
     573
    560574                        <dt><?php esc_html_e( 'Manage Opt-outs', 'buddypress' ) ?></dt>
    561575                        <dd>
    562576                                <?php esc_html_e( 'BuddyPress stores opt-out requests from people who are not members of this site, but have been contacted via communication from this site, and wish to opt-out from future communication.', 'buddypress' ); ?>
    563577                                <?php
    564578                                $url = add_query_arg( 'page', 'bp-optouts', bp_get_admin_url( $page ) );
    565579                                printf(
    566                                         /* translators: %s: the link to the BuddyPress Nonmember Opt-outs */
     580                                        /* translators: %s: the link to the BuddyPress Nonmember Opt-outs management tool screen */
    567581                                        esc_html_x( 'Visit %s to manage your site&rsquo;s opt-out requests.', 'buddypress opt-outs intro', 'buddypress' ),
    568582                                        '<a href="' . esc_url( $url ) . '">' . esc_html__( 'Nonmember Opt-outs', 'buddypress' ) . '</a>'
    569583                                );
  • src/bp-core/bp-core-filters.php

    diff --git src/bp-core/bp-core-filters.php src/bp-core/bp-core-filters.php
    index 735c7090a..219816b05 100644
    function bp_email_set_default_headers( $headers, $property, $transform, $email ) 
    10681068        // Add 'List-Unsubscribe' header if applicable.
    10691069        if ( ! empty( $tokens['unsubscribe'] ) && $tokens['unsubscribe'] !== wp_login_url() ) {
    10701070                $user = get_user_by( 'email', $tokens['recipient.email'] );
     1071                $user_id = isset( $user->ID ) ? $user->ID : 0;
    10711072
    1072                 $link = bp_email_get_unsubscribe_link( array(
    1073                         'user_id'           => $user->ID,
     1073                $args = array(
     1074                        'user_id'           => $user_id,
    10741075                        'notification_type' => $email->get( 'type' ),
    1075                 ) );
     1076                );
     1077                // If this email is not to a current member, include the nonmember's email address.
     1078                if ( ! $user_id ) {
     1079                        $args['email_address'] = $tokens['recipient.email'];
     1080                }
     1081
     1082                $link = bp_email_get_unsubscribe_link( $args );
    10761083
    10771084                if ( ! empty( $link ) ) {
    10781085                        $headers['List-Unsubscribe'] = sprintf( '<%s>', esc_url_raw( $link ) );
  • src/bp-core/bp-core-functions.php

    diff --git src/bp-core/bp-core-functions.php src/bp-core/bp-core-functions.php
    index 1535afed4..854a1982d 100644
    function bp_core_add_page_mappings( $components, $existing = 'keep' ) { 
    702702
    703703        // Register and Activate are not components, but need pages when
    704704        // registration is enabled.
    705         if ( bp_get_signup_allowed() ) {
     705        if ( bp_get_signup_allowed() || bp_get_members_invitations_allowed()  ) {
    706706                foreach ( array( 'register', 'activate' ) as $slug ) {
    707707                        if ( ! isset( $pages[ $slug ] ) ) {
    708708                                $pages_to_create[ $slug ] = $page_titles[ $slug ];
    function bp_send_email( $email_type, $to, $args = array() ) { 
    34723472        // From, subject, content are set automatically.
    34733473        if ( 'settings-verify-email-change' === $email_type && isset( $args['tokens']['displayname'] ) ) {
    34743474                $email->set_to( $to, $args['tokens']['displayname'] );
     3475        // Emails sent to nonmembers will have no recipient.name populated.
     3476        } else if ( 'bp-members-invitation' === $email_type ) {
     3477                $email->set_to( $to, $to );
    34753478        } else {
    34763479                $email->set_to( $to );
    34773480        }
    function bp_email_get_schema() { 
    38153818                        /* translators: do not remove {} brackets or translate its contents. */
    38163819                        'post_title'   => __( '[{{{site.name}}}] You have an invitation to the group: "{{group.name}}"', 'buddypress' ),
    38173820                        /* translators: do not remove {} brackets or translate its contents. */
    3818                         'post_content' => __( "<a href=\"{{{inviter.url}}}\">{{inviter.name}}</a> has invited you to join the group: &quot;{{group.name}}&quot;.\n{{invite.message}}\n<a href=\"{{{invites.url}}}\">Go here to accept your invitation</a> or <a href=\"{{{group.url}}}\">visit the group</a> to learn more.", 'buddypress' ),
     3821                        'post_content' => __( "<a href=\"{{{inviter.url}}}\">{{inviter.name}}</a> has invited you to join the group: &quot;{{group.name}}&quot;.\n\n{{invite.message}}\n\n<a href=\"{{{invites.url}}}\">Go here to accept your invitation</a> or <a href=\"{{{group.url}}}\">visit the group</a> to learn more.", 'buddypress' ),
    38193822                        /* translators: do not remove {} brackets or translate its contents. */
    3820                         'post_excerpt' => __( "{{inviter.name}} has invited you to join the group: \"{{group.name}}\".\n\nTo accept your invitation, visit: {{{invites.url}}}\n\nTo learn more about the group, visit: {{{group.url}}}.\nTo view {{inviter.name}}'s profile, visit: {{{inviter.url}}}", 'buddypress' ),
     3823                        'post_excerpt' => __( "{{inviter.name}} has invited you to join the group: \"{{group.name}}\".\n\n{{invite.message}}\n\nTo accept your invitation, visit: {{{invites.url}}}\n\nTo learn more about the group, visit: {{{group.url}}}.\nTo view {{inviter.name}}'s profile, visit: {{{inviter.url}}}", 'buddypress' ),
    38213824                ),
    38223825                'groups-member-promoted' => array(
    38233826                        /* translators: do not remove {} brackets or translate its contents. */
    function bp_email_get_schema() { 
    38673870                        /* translators: do not remove {} brackets or translate its contents. */
    38683871                        'post_excerpt' => __( "Your membership request for the group \"{{group.name}}\" has been rejected.\n\nTo request membership again, visit: {{{group.url}}}", 'buddypress' ),
    38693872                ),
     3873                'bp-members-invitation' => array(
     3874                        /* translators: do not remove {} brackets or translate its contents. */
     3875                        'post_title'   => __( '{{inviter.name}} has invited you to join {{site.name}}', 'buddypress' ),
     3876                        /* translators: do not remove {} brackets or translate its contents. */
     3877                        'post_content' => __( "<a href=\"{{{inviter.url}}}\">{{inviter.name}}</a> has invited you to join the site: &quot;{{site.name}}&quot;.\n\n{{usermessage}}\n\n<a href=\"{{{invite.accept_url}}}\">Accept your invitation</a> or <a href=\"{{{site.url}}}\">visit the site</a> to learn more.", 'buddypress' ),
     3878                        /* translators: do not remove {} brackets or translate its contents. */
     3879                        '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' ),
     3880                ),
    38703881        ) );
    38713882}
    38723883
    function bp_email_get_type_schema( $field = 'description' ) { 
    40124023                'description'   => __( 'Recipient has successfully activated an account.', 'buddypress' ),
    40134024        );
    40144025
     4026        $members_invitation = array(
     4027                'description'   => __( 'A site member has sent a site invitation to the recipient.', 'buddypress' ),
     4028                'unsubscribe'   => array(
     4029                        'meta_key'      => 'notification_bp_members_invite',
     4030                        'message'       => __( 'You will no longer receive emails when you are invited to join a site.', 'buddypress' ),
     4031                ),
     4032        );
     4033
    40154034        $types = array(
    40164035                'activity-comment'                   => $activity_comment,
    40174036                'activity-comment-author'            => $activity_comment_author,
    function bp_email_get_type_schema( $field = 'description' ) { 
    40304049                'groups-membership-request-accepted' => $groups_membership_request_accepted,
    40314050                'groups-membership-request-rejected' => $groups_membership_request_rejected,
    40324051                'core-user-activation'               => $core_user_activation,
     4052                'bp-members-invitation'              => $members_invitation,
    40334053        );
    40344054
    40354055        if ( $field !== 'all' ) {
    function bp_email_unsubscribe_handler() { 
    40494069        $raw_email_type = ! empty( $_GET['nt'] ) ? $_GET['nt'] : '';
    40504070        $raw_hash       = ! empty( $_GET['nh'] ) ? $_GET['nh'] : '';
    40514071        $raw_user_id    = ! empty( $_GET['uid'] ) ? absint( $_GET['uid'] ) : 0;
    4052         $new_hash       = hash_hmac( 'sha1', "{$raw_email_type}:{$raw_user_id}", bp_email_get_salt() );
     4072        $raw_user_email = ! empty( $_GET['uem'] ) ? $_GET['uem'] : '';
     4073
     4074        $new_hash = '';
     4075        if ( ! empty( $raw_user_id ) ) {
     4076                $new_hash = hash_hmac( 'sha1', "{$raw_email_type}:{$raw_user_id}", bp_email_get_salt() );
     4077        } else if ( ! empty( $raw_user_email ) ) {
     4078                $new_hash = hash_hmac( 'sha1', "{$raw_email_type}:{$raw_user_email}", bp_email_get_salt() );
     4079        }
    40534080
    40544081        // Check required values.
    4055         if ( ! $raw_user_id || ! $raw_email_type || ! $raw_hash || ! array_key_exists( $raw_email_type, $emails ) ) {
     4082        if ( ( ! $raw_user_id && ! $raw_user_email ) || ! $raw_email_type || ! $raw_hash || ! array_key_exists( $raw_email_type, $emails ) ) {
    40564083                $redirect_to = wp_login_url();
    40574084                $result_msg  = __( 'Something has gone wrong.', 'buddypress' );
    40584085                $unsub_msg   = __( 'Please log in and go to your settings to unsubscribe from notification emails.', 'buddypress' );
    function bp_email_unsubscribe_handler() { 
    40784105                        $redirect_to = bp_core_get_user_domain( get_current_user_id() );
    40794106                }
    40804107
     4108        // This is an unsubscribe request from a nonmember.
     4109        } else if ( $raw_user_email ) {
     4110                // Unsubscribe.
     4111                error_log( 'caught unsubscribe request {$raw_user_email}' );
     4112                // @TODO: Create opt-out once opt-out logic is in place.
     4113                $redirect_to = home_url();
     4114
     4115                $result_msg = $emails[ $raw_email_type ]['unsubscribe']['message'];
     4116                $unsub_msg  = __( 'You have been unsubscribed.' );
     4117        // This is an unsubscribe request from a current member.
    40814118        } else {
    40824119                if ( bp_is_active( 'settings' ) ) {
    40834120                        $redirect_to = sprintf(
    function bp_email_unsubscribe_handler() { 
    41054142        );
    41064143
    41074144        bp_core_add_message( $message );
    4108         bp_core_redirect( bp_core_get_user_domain( $raw_user_id ) );
     4145
     4146        $page_redirect = '';
     4147        if ( $raw_user_id  ) {
     4148                $page_redirect = bp_core_get_user_domain( $raw_user_id );
     4149        }
     4150
     4151        bp_core_redirect();
    41094152
    41104153        exit;
    41114154}
    function bp_email_unsubscribe_handler() { 
    41224165 *    @type string $notification_type Which notification type is being sent.
    41234166 *    @type string $user_id           The ID of the user to whom the notification is sent.
    41244167 *    @type string $redirect_to       Optional. The url to which the user will be redirected. Default is the activity directory.
     4168 *    @type string $email             Optional. The email address of the user to whom the notification is sent.
    41254169 * }
    41264170 * @return string The unsubscribe link.
    41274171 */
    function bp_email_get_unsubscribe_link( $args ) { 
    41414185                return '';
    41424186        }
    41434187
    4144         $link = add_query_arg(
    4145                 array(
    4146                         'action' => 'unsubscribe',
    4147                         'nh'     => hash_hmac( 'sha1', "{$email_type}:{$user_id}", bp_email_get_salt() ),
    4148                         'nt'     => $args['notification_type'],
    4149                         'uid'    => $user_id,
    4150                 ),
    4151                 $redirect_to
    4152         );
     4188        $link = '';
     4189        // Case where the recipient is a member of the site.
     4190        if ( ! empty( $user_id ) ) {
     4191                $link = add_query_arg(
     4192                        array(
     4193                                'action' => 'unsubscribe',
     4194                                'nh'     => hash_hmac( 'sha1', "{$email_type}:{$user_id}", bp_email_get_salt() ),
     4195                                'nt'     => $args['notification_type'],
     4196                                'uid'    => $user_id,
     4197                        ),
     4198                        $redirect_to
     4199                );
     4200        // Case where the recipient is not a member of the site.
     4201        } else if ( ! empty( $args['email_address'] ) ) {
     4202                $email_address = $args['email_address'];
     4203                $link = add_query_arg(
     4204                        array(
     4205                                'action' => 'unsubscribe',
     4206                                'nh'     => hash_hmac( 'sha1', "{$email_type}:{$email_address}", bp_email_get_salt() ),
     4207                                'nt'     => $args['notification_type'],
     4208                                'uid'    => $user_id,
     4209                                'uem'    => $email_address,
     4210                        ),
     4211                        $redirect_to
     4212                );
     4213        }
    41534214
    41544215        /**
    41554216         * Filters the unsubscribe link.
    function bp_get_optouts( $args = array() ) { 
    43604421        return $optout_class::get( $args );
    43614422}
    43624423
     4424/**
     4425 * Check an email address to see if that individual has opted out.
     4426 *
     4427 * @since 8.0.0
     4428 *
     4429 * @param string $email_address Email address to check.
     4430 * @return bool True if the user has opted out, false otherwise.
     4431 */
     4432function bp_user_has_opted_out( $email_address = '' ) {
     4433        $optout_class = new BP_Optout();
     4434        $optout_id    = $optout_class->optout_exists(
     4435                array(
     4436                        'email_address' => $email_address,
     4437                )
     4438        );
     4439        return (bool) $optout_id;
     4440}
     4441
    43634442/**
    43644443 * Delete a BP_Optout by ID.
    43654444 *
  • src/bp-core/bp-core-template.php

    diff --git src/bp-core/bp-core-template.php src/bp-core/bp-core-template.php
    index 799a69cdc..e840a7cd6 100644
    function bp_is_settings_component() { 
    22472247        return (bool) bp_is_current_component( 'settings' );
    22482248}
    22492249
     2250/**
     2251 * Check whether the current page is an Invitations screen.
     2252 *
     2253 * @since 8.0.0
     2254 *
     2255 * @return bool True if the current page is an Invitations screen.
     2256 */
     2257function bp_is_members_invitations_screen() {
     2258        return (bool) bp_is_current_component( bp_get_members_invitations_slug() );
     2259}
     2260
    22502261/**
    22512262 * Is the current component an active core component?
    22522263 *
    function bp_is_user_settings_profile() { 
    26472658        return (bool) ( bp_is_user_settings() && bp_is_current_action( 'profile' ) );
    26482659}
    26492660
     2661/**
     2662 * Is the current page a user's community invitations page?
     2663 *
     2664 * Eg http://example.com/members/cassie/invitations/ (or a subpage thereof).
     2665 *
     2666 * @since 8.0.0
     2667 *
     2668 * @return bool True if the current page is a user's community invitations page.
     2669 */
     2670function bp_is_user_members_invitations() {
     2671        return (bool) ( bp_is_user() && bp_is_members_invitations_screen() );
     2672}
     2673
     2674/**
     2675 * Is the current page a user's List Invites page?
     2676 *
     2677 * Eg http://example.com/members/cassie/invitations/list-invites/.
     2678 *
     2679 * @since 8.0.0
     2680 *
     2681 * @return bool True if the current page is a user's List Invites page.
     2682 */
     2683function bp_is_user_members_invitations_list() {
     2684        return (bool) ( bp_is_user_members_invitations() && bp_is_current_action( 'list-invites' ) );
     2685}
     2686
     2687/**
     2688 * Is the current page a user's Send Invites page?
     2689 *
     2690 * Eg http://example.com/members/cassie/invitations/send-invites/.
     2691 *
     2692 * @since 8.0.0
     2693 *
     2694 * @return bool True if the current page is a user's Send Invites page.
     2695 */
     2696function bp_is_user_members_invitations_send_screen() {
     2697        return (bool) ( bp_is_user_members_invitations() && bp_is_current_action( 'send-invites' ) );
     2698}
     2699
    26502700/** Groups ********************************************************************/
    26512701
    26522702/**
  • 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 1e10deab7..bbf945471 100644
    class BP_Admin { 
    411411                        register_setting( 'buddypress', 'bp-disable-cover-image-uploads', 'intval' );
    412412                }
    413413
     414                // Community Invitations.
     415                if ( bp_is_active( 'members', 'invitations' ) ) {
     416                        add_settings_field( 'bp-enable-members-invitations', __( 'Invitations', 'buddypress' ), 'bp_admin_setting_callback_members_invitations', 'buddypress', 'bp_members' );
     417                        register_setting( 'buddypress', 'bp-enable-members-invitations', 'intval' );
     418                }
     419
    414420                /* XProfile Section **************************************************/
    415421
    416422                if ( bp_is_active( 'xprofile' ) ) {
  • 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 0b7d2f009..726a78da4 100644
    abstract class BP_Invitation_Manager { 
    9999                        return false;
    100100                }
    101101
     102                // If an email address is specified, it must be a valid email address.
     103                if ( $r['invitee_email'] && ! is_email( $r['invitee_email'] ) ) {
     104                        return false;
     105                }
     106
    102107                /**
    103108                 * Is this user allowed to extend invitations in this situation?
    104109                 *
    abstract class BP_Invitation_Manager { 
    157162         * @param int   $invitation_id ID of invitation to send.
    158163         * @param array $args          See BP_Invitation::mark_sent().
    159164         *
    160          * @return int|bool The number of rows updated, or false on error.
     165         * @return bool The result of `run_send_action()`.
    161166         */
    162167        public function send_invitation_by_id( $invitation_id = 0, $args = array() ) {
    163168                $updated = false;
    abstract class BP_Invitation_Manager { 
    195200                }
    196201
    197202                // Perform the send action.
    198                 $this->run_send_action( $invitation );
     203                $success = $this->run_send_action( $invitation );
    199204
    200                 $updated = BP_Invitation::mark_sent( $invitation->id, $args );
     205                if ( $success ) {
     206                        BP_Invitation::mark_sent( $invitation->id, $args );
     207                }
    201208
    202                 return $updated;
     209                return $success;
    203210        }
    204211
    205212        /**
    abstract class BP_Invitation_Manager { 
    309316         * @param int   $request_id ID of request to send.
    310317         * @param array $args       See BP_Invitation::mark_sent().
    311318         *
    312          * @return int|bool The number of rows updated, or false on error.
     319         * @return bool The result of `run_send_action()`.
    313320         */
    314321        public function send_request_notification_by_id( $request_id = 0, $args = array() ) {
    315322                $updated = false;
    abstract class BP_Invitation_Manager { 
    342349                }
    343350
    344351                // Perform the send action.
    345                 $this->run_send_action( $request );
     352                $success = $this->run_send_action( $request );
    346353
    347                 $updated = BP_Invitation::mark_sent( $request->id, $args );
     354                if ( $success ) {
     355                        BP_Invitation::mark_sent( $request->id, $args );
     356                }
    348357
    349                 return $updated;
     358                return $success;
    350359        }
    351360
    352361        /** Retrieve ******************************************************************/
    abstract class BP_Invitation_Manager { 
    383392                return BP_Invitation::get( $args );
    384393        }
    385394
     395        /**
     396         * Get a count of the number of invitations that match provided filter parameters.
     397         *
     398         * @since 8.0.0
     399         *
     400         * @see BP_Invitation::get_total_count() for a description of accepted parameters.
     401         *
     402         * @return int Total number of invitations.
     403         */
     404        public function get_invitations_total_count( $args = array() ) {
     405                // Default to returning invitations, not requests.
     406                if ( empty( $args['type'] ) ) {
     407                        $args['type'] = 'invite';
     408                }
     409                // Use the class_name property value.
     410                $args['class'] = $this->class_name;
     411
     412                return BP_Invitation::get_total_count( $args );
     413        }
     414
    386415        /**
    387416         * Get requests, based on provided filter parameters.
    388417         *
    abstract class BP_Invitation_Manager { 
    703732                ) );
    704733        }
    705734
     735        /**
     736         * Delete an invitation by id.
     737         *
     738         * @since 8.0.0
     739         *
     740         * @param int $id ID of the invitation to delete.
     741         * @return int|bool Number of rows deleted on success, false on failure.
     742         */
     743        public function delete_by_id( $id ) {
     744                // Ensure that the invitation exists and was created by this class.
     745                $invite = new BP_Invitation( $id );
     746                if ( ! $invite->id || sanitize_key( $this->class_name ) !== $invite->class ) {
     747                        return false;
     748                }
     749
     750                return BP_Invitation::delete_by_id( $id );
     751        }
     752
     753
     754
    706755        /**
    707756         * This is where custom actions are added (in child classes)
    708757         * to determine whether an invitation should be allowed.
  • src/bp-members/admin/bp-members-admin-classes.php

    diff --git src/bp-members/admin/bp-members-admin-classes.php src/bp-members/admin/bp-members-admin-classes.php
    index 96e339e7a..4e2e79b54 100644
    defined( 'ABSPATH' ) || exit; 
    1212
    1313if ( class_exists( 'WP_Users_List_Table' ) ) {
    1414        require dirname( dirname( __FILE__ ) ) . '/classes/class-bp-members-list-table.php';
     15        require dirname( dirname( __FILE__ ) ) . '/classes/class-bp-members-invitations-list-table.php';
    1516}
    1617
    1718if ( class_exists( 'WP_MS_Users_List_Table' ) ) {
  • src/bp-members/bp-members-activity.php

    diff --git src/bp-members/bp-members-activity.php src/bp-members/bp-members-activity.php
    index 8907d0a7e..c905ff79c 100644
    add_action( 'bp_register_activity_actions', 'bp_members_register_activity_action 
    5656 * @return string $action
    5757 */
    5858function bp_members_format_activity_action_new_member( $action, $activity ) {
    59         $userlink = bp_core_get_userlink( $activity->user_id );
     59        $userlink         = bp_core_get_userlink( $activity->user_id );
     60        $inviter_userlink = false;
     61        if ( $invite_id = bp_get_user_meta( $activity->user_id, 'accepted_members_invitation', true ) ) {
     62                $invite           = new BP_Invitation( (int) $invite_id );
     63                if ( $invite->inviter_id ) {
     64                        $inviter_userlink = bp_core_get_userlink( $invite->inviter_id );
     65                }
     66        }
    6067
    61         /* translators: %s: user link */
    62         $action = sprintf( esc_html__( '%s became a registered member', 'buddypress' ), $userlink );
     68        if ( $inviter_userlink ) {
     69                $action           = sprintf(
     70                        /* translators: 1: new user link. 2: inviter user link. */
     71                        esc_html__( '%1$s accepted an invitation from %2$s and became a registered member', 'buddypress' ),
     72                        $userlink,
     73                        $inviter_userlink
     74                );
     75        } else {
     76                /* translators: %s: user link */
     77                $action = sprintf( esc_html__( '%s became a registered member', 'buddypress' ), $userlink );
     78        }
    6379
    6480        // Legacy filter - pass $user_id instead of $activity.
    6581        if ( has_filter( 'bp_core_activity_registered_member_action' ) ) {
  • src/bp-members/bp-members-adminbar.php

    diff --git src/bp-members/bp-members-adminbar.php src/bp-members/bp-members-adminbar.php
    index 10f784d09..f9b45ad38 100644
    function bp_members_remove_edit_page_menu() { 
    178178        }
    179179}
    180180add_action( 'add_admin_bar_menus', 'bp_members_remove_edit_page_menu' );
     181
     182/**
     183 * Add the "Invitations" menu and submenus.
     184 *
     185 * @since 8.0.0
     186 */
     187function bp_members_admin_bar_add_invitations_menu() {
     188        global $wp_admin_bar;
     189
     190        // Bail if this is an ajax request.
     191        if ( defined( 'DOING_AJAX' ) ) {
     192                return;
     193        }
     194
     195        if ( is_user_logged_in() && bp_get_members_invitations_allowed() && ( bp_current_user_can( 'bp_members_send_invitation' ) || bp_members_invitations_user_has_sent_invites() ) ) {
     196                $bp               = buddypress();
     197                $invitations_link = trailingslashit( bp_loggedin_user_domain() . bp_get_members_invitations_slug() );
     198
     199                $wp_admin_bar->add_node(
     200                        array(
     201                                'id'     => $bp->my_account_menu_id . '-invitations',
     202                                'parent' => $bp->my_account_menu_id,
     203                                'title'  => __( 'Invitations', 'buddypress' ),
     204                                'href'   => $invitations_link,
     205                                'meta'   => array(
     206                                        'class'  => 'ab-sub-secondary'
     207                                )
     208                        )
     209                );
     210
     211                if ( bp_current_user_can( 'bp_members_send_invitation' ) ) {
     212                        $wp_admin_bar->add_node(
     213                                array(
     214                                        'id'     => $bp->my_account_menu_id . '-invitations-send',
     215                                        'parent' => $bp->my_account_menu_id . '-invitations',
     216                                        'title'  => __( 'Send Invites', 'buddypress' ),
     217                                        'href'   => $invitations_link,
     218                                        'meta'   => array(
     219                                                'class'  => 'ab-sub-secondary'
     220                                        )
     221                                )
     222                        );
     223                }
     224
     225                $wp_admin_bar->add_node(
     226                        array(
     227                                'id'     => $bp->my_account_menu_id . '-invitations-list',
     228                                'parent' => $bp->my_account_menu_id . '-invitations',
     229                                'title'  => __( 'Pending Invites', 'buddypress' ),
     230                                'href'   => $invitations_link . 'list-invites/',
     231                                'meta'   => array(
     232                                        'class'  => 'ab-sub-secondary'
     233                                )
     234                        )
     235                );
     236        }
     237}
     238add_action( 'bp_setup_admin_bar', 'bp_members_admin_bar_add_invitations_menu', 90 );
  • src/bp-members/bp-members-filters.php

    diff --git src/bp-members/bp-members-filters.php src/bp-members/bp-members-filters.php
    index 5575ebef2..38f3433e6 100644
    function bp_members_edit_profile_url( $url, $user_id, $scheme = 'admin' ) { 
    127127        return apply_filters( 'bp_members_edit_profile_url', $profile_link, $url, $user_id, $scheme );
    128128}
    129129add_filter( 'edit_profile_url', 'bp_members_edit_profile_url', 10, 3 );
     130
     131/**
     132 * Filter the bp_user_can value to determine what the user can do in the members component.
     133 *
     134 * @since 8.0.0
     135 *
     136 * @param bool   $retval     Whether or not the current user has the capability.
     137 * @param int    $user_id
     138 * @param string $capability The capability being checked for.
     139 * @param int    $site_id    Site ID. Defaults to the BP root blog.
     140 * @param array  $args       Array of extra arguments passed.
     141 *
     142 * @return bool
     143 */
     144function bp_members_user_can_filter( $retval, $user_id, $capability, $site_id, $args ) {
     145
     146        switch ( $capability ) {
     147                case 'bp_members_manage_membership_requests':
     148                        $retval = bp_user_can( $user_id, 'bp_moderate' );
     149                        break;
     150                case 'bp_members_send_invitation':
     151                        // @todo Add restrictions?
     152                        if ( bp_get_members_invitations_allowed() ) {
     153                                $retval = true;
     154                        }
     155                        break;
     156                case 'bp_members_receive_invitation':
     157                        if ( bp_get_members_invitations_allowed() ) {
     158                                $retval = true;
     159                                // The invited user must not already be a member of the network.
     160                                if ( empty( $args['invitee_email'] ) || false !== get_user_by( 'email', $args['invitee_email'] ) ) {
     161                                        $retval = false;
     162                                }
     163                        // @TODO: The invited user must not have opted out from receiving invitations.
     164                        }
     165
     166                        break;
     167        }
     168
     169        return $retval;
     170
     171}
     172add_filter( 'bp_user_can', 'bp_members_user_can_filter', 10, 5 );
     173
     174/**
     175 * Do not allow the new user to change the email address
     176 * if they are accepting a community invitation.
     177 *
     178 * @since 8.0.0
     179 *
     180 * @param array  $attributes The field attributes.
     181 * @param string $name       The field name.
     182 *
     183 * @return array $attributes The field attributes.
     184 */
     185function bp_members_invitations_make_registration_email_input_readonly_if_invite( $attributes, $name ) {
     186        if ( 'email' === $name && bp_get_members_invitations_allowed() ) {
     187                $invite = bp_get_members_invitation_from_request();
     188                if ( $invite->id ) {
     189                        $attributes['readonly'] = 'readonly';
     190                }
     191        }
     192        return $attributes;
     193}
     194add_filter( 'bp_get_form_field_attributes', 'bp_members_invitations_make_registration_email_input_readonly_if_invite', 10, 2 );
     195
     196/**
     197 * Provide a more-specific welcome message if the new user
     198 * is accepting a network invitation.
     199 *
     200 * @since 8.0.0
     201 *
     202 * @return string $message The message text.
     203 */
     204function bp_members_invitations_get_registration_welcome_message() {
     205        $message = '';
     206        if ( ! bp_get_members_invitations_allowed() ) {
     207                return $message;
     208        }
     209        $invite = bp_get_members_invitation_from_request();
     210        if ( ! $invite->id ) {
     211                return $message;
     212        }
     213
     214        // Fetch the display names of all inviters to personalize the welcome message.
     215        $all_invites = bp_members_invitations_get_invites(
     216                array(
     217                        'invitee_email' => $invite->invitee_email,
     218                        'invite_sent'   => 'sent',
     219                )
     220        );
     221        $inviters = array();
     222        foreach ( $all_invites as $inv ) {
     223                $inviters[] = bp_core_get_user_displayname( $inv->inviter_id );
     224        }
     225
     226        if ( ! empty( $inviters ) ) {
     227                $message = sprintf( _n( 'Welcome! You&#8217;ve been invited to join the site by the following user: %s. ', 'Welcome! You&#8217;ve been invited to join the site by the following users: %s. ', count( $inviters ), 'buddypress' ), implode( ', ', $inviters ) );
     228        } else {
     229                $message = __( 'Welcome! You&#8217;ve been invited to join the site. ', 'buddypress' );
     230        }
     231        return $message;
     232}
     233
     234/**
     235 * Provide a more-specific "registration is disabled" message
     236 * if registration is available by invitation only.
     237 * Also provide failure note if new user is trying to accept
     238 * a network invitation but there's a problem.
     239 *
     240 * @since 8.0.0
     241 *
     242 * @return string $message The message text.
     243 */
     244function bp_members_invitations_get_modified_registration_disabled_message() {
     245        $message = '';
     246        if ( bp_get_members_invitations_allowed() ) {
     247                $message = __( 'Member registration is allowed by invitation only.', 'buddypress' );
     248                // Is the user trying to accept an invitation but something is wrong?
     249                if ( ! empty( $_GET['inv'] ) ) {
     250                        $message .= __( ' It looks like there is a problem with your invitation. Please try again.', 'buddypress' );
     251                }
     252        }
     253        return $message;
     254}
     255
     256/**
     257 * Sanitize the invitation property output.
     258 *
     259 * @since 8.0.0
     260 *
     261 * @param int|string $value    The value for the requested property.
     262 * @param string     $property The name of the requested property.
     263 * @param string     $context  The context of display.
     264 * @return int|string          The sanitized value.
     265 */
     266function bp_members_sanitize_invitation_property( $value = '', $property = '', $context = 'html' ) {
     267        if ( ! $property ) {
     268                return '';
     269        }
     270
     271        switch ( $property ) {
     272                case 'id':
     273                case 'user_id':
     274                case 'item_id':
     275                case 'secondary_item_id':
     276                        $value = absint( $value );
     277                        break;
     278                case 'invite_sent':
     279                case 'accepted':
     280                        $value = absint( $value ) ? __( 'Yes', 'buddypress' ) : __( 'No', 'buddypress' );
     281                        $value = 'attribute' === $context ? esc_attr( $value ) : esc_html( $value );
     282                        break;
     283                case 'invitee_email':
     284                        $value = sanitize_email( $value );
     285                        break;
     286                case 'content':
     287                        $value = wp_kses( $value, array() );
     288                        $value = wptexturize( $value );
     289                        break;
     290                case 'date_modified':
     291                        $value = mysql2date( 'Y/m/d g:i:s a', $value );
     292                        $value = 'attribute' === $context ? esc_attr( $value ) : esc_html( $value );
     293                        break;
     294
     295                default:
     296                        $value = 'attribute' === $context ? esc_attr( $value ) : esc_html( $value );
     297                        break;
     298        }
     299
     300        return $value;
     301}
     302add_filter( 'bp_the_members_invitation_property', 'bp_members_sanitize_invitation_property', 10, 3 );
  • src/bp-members/bp-members-functions.php

    diff --git src/bp-members/bp-members-functions.php src/bp-members/bp-members-functions.php
    index 347614fc8..b6940ebb1 100644
    function bp_send_welcome_email( $user_id = 0 ) { 
    33423342        bp_send_email( 'core-user-activation', $user_id, array( 'tokens' => $welcome_tokens ) );
    33433343}
    33443344add_action( 'bp_core_activated_user', 'bp_send_welcome_email', 10, 1 );
     3345
     3346/**
     3347 * Get invitations to the BP community filtered by arguments.
     3348 *
     3349 * @since 8.0.0
     3350 *
     3351 * @param array $args     Invitation arguments.
     3352 *                        See BP_Invitation::get() for list.
     3353 *
     3354 * @return array $invites     Matching BP_Invitation objects.
     3355 */
     3356function bp_members_invitations_get_invites( $args = array() ) {
     3357        $invites_class = new BP_Members_Invitation_Manager();
     3358        return $invites_class->get_invitations( $args );
     3359}
     3360
     3361/**
     3362 * Check whether a user has sent any community invitations.
     3363 *
     3364 * @since 8.0.0
     3365 *
     3366 * @param int $user_id ID of user to check for invitations sent by.
     3367 *                     Defaults to the current user's ID.
     3368 *
     3369 * @return bool $invites True if user has sent invites.
     3370 */
     3371function bp_members_invitations_user_has_sent_invites( $user_id = 0 ) {
     3372        if ( 0 === $user_id ) {
     3373                $user_id = bp_loggedin_user_id();
     3374                if ( ! $user_id ) {
     3375                        return false;
     3376                }
     3377        }
     3378        $invites_class = new BP_Members_Invitation_Manager();
     3379        $args = array(
     3380                'inviter_id' => $user_id,
     3381        );
     3382        return (bool) $invites_class->invitation_exists( $args );
     3383}
     3384
     3385/**
     3386 * Invite a user to a BP community.
     3387 *
     3388 * @since 8.0.0
     3389 *
     3390 * @param array|string $args {
     3391 *     Array of arguments.
     3392 *     @type int    $invitee_email Email address of the user being invited.
     3393 *     @type int    $network_id    ID of the network to which the user is being invited.
     3394 *     @type int    $inviter_id    Optional. ID of the inviting user. Default:
     3395 *                                 ID of the logged-in user.
     3396 *     @type string $date_modified Optional. Modified date for the invitation.
     3397 *                                 Default: current date/time.
     3398 *     @type string $content       Optional. Message to invitee.
     3399 *     @type bool   $send_invite   Optional. Whether the invitation should be
     3400 *                                 sent now. Default: false.
     3401 * }
     3402 * @return bool True on success, false on failure.
     3403 */
     3404function bp_members_invitations_invite_user( $args ) {
     3405        $r = bp_parse_args( $args, array(
     3406                'invitee_email' => '',
     3407                'network_id'    => get_current_network_id(),
     3408                'inviter_id'    => bp_loggedin_user_id(),
     3409                'date_modified' => bp_core_current_time(),
     3410                'content'       => '',
     3411                'send_invite'   => 0
     3412        ), 'community_invite_user' );
     3413
     3414        $inv_args = array(
     3415                'invitee_email' => $r['invitee_email'],
     3416                'item_id'       => $r['network_id'],
     3417                'inviter_id'    => $r['inviter_id'],
     3418                'date_modified' => $r['date_modified'],
     3419                'content'       => $r['content'],
     3420                'send_invite'   => $r['send_invite']
     3421        );
     3422
     3423        // Create the invitataion.
     3424        $invites_class = new BP_Members_Invitation_Manager();
     3425        $created       = $invites_class->add_invitation( $inv_args );
     3426
     3427        /**
     3428         * Fires after the creation of a new network invite.
     3429         *
     3430         * @since 8.0.0
     3431         *
     3432         * @param array    $r       Array of parsed arguments for the network invite.
     3433         * @param int|bool $created The ID of the invitation or false if it couldn't be created.
     3434         */
     3435        do_action( 'bp_members_invitations_invite_user', $r, $created );
     3436
     3437        return $created;
     3438}
     3439
     3440/**
     3441 * Resend a membership invitation email by id.
     3442 *
     3443 * @since 8.0.0
     3444 *
     3445 * @param int $id ID of the invitation to resend.
     3446 * @return bool True on success, false on failure.
     3447 */
     3448function bp_members_invitation_resend_by_id( $id = 0 ) {
     3449
     3450        // Find the invitation before deleting it.
     3451        $existing_invite = new BP_Invitation( $id );
     3452        $invites_class   = new BP_Members_Invitation_Manager();
     3453        $success         = $invites_class->send_invitation_by_id( $id );
     3454
     3455        if ( ! $success ) {
     3456                return $success;
     3457        }
     3458
     3459        /**
     3460         * Fires after the re-sending of a network invite.
     3461         *
     3462         * @since 8.0.0
     3463         *
     3464         * @param BP_Invitation $existing_invite The invitation that was resent.
     3465         */
     3466        do_action( 'bp_members_invitations_resend_invitation', $existing_invite );
     3467
     3468        return $success;
     3469}
     3470
     3471/**
     3472 * Delete a membership invitation by id.
     3473 *
     3474 * @since 8.0.0
     3475 *
     3476 * @param int $id ID of the invitation to delete.
     3477 * @return int|bool Number of rows deleted on success, false on failure.
     3478 */
     3479function bp_members_invitations_delete_by_id( $id = 0 ) {
     3480
     3481        // Find the invitation before deleting it.
     3482        $existing_invite = new BP_Invitation( $id );
     3483        $invites_class   = new BP_Members_Invitation_Manager();
     3484        $success         = $invites_class->delete_by_id( $id );
     3485
     3486        if ( ! $success ) {
     3487                return $success;
     3488        }
     3489
     3490        // Run a different action depending on the status of the invite.
     3491        if ( ! $existing_invite->invite_sent ) {
     3492                /**
     3493                 * Fires after the deletion of an unsent community invite.
     3494                 *
     3495                 * @since 8.0.0
     3496                 *
     3497                 * @param BP_Invitation $existing_invite The invitation to be deleted.
     3498                 */
     3499                do_action( 'bp_members_invitations_canceled_invitation', $existing_invite );
     3500        } else if ( ! $existing_invite->accepted ) {
     3501                /**
     3502                 * Fires after the deletion of a sent, but not yet accepted, community invite.
     3503                 *
     3504                 * @since 8.0.0
     3505                 *
     3506                 * @param BP_Invitation $existing_invite The invitation to be deleted.
     3507                 */
     3508                do_action( 'bp_members_invitations_revoked_invitation', $existing_invite );
     3509        } else {
     3510                /**
     3511                 * Fires after the deletion of a sent and accepted community invite.
     3512                 *
     3513                 * @since 8.0.0
     3514                 *
     3515                 * @param BP_Invitation $existing_invite The invitation to be deleted.
     3516                 */
     3517                do_action( 'bp_members_invitations_deleted_invitation', $existing_invite );
     3518        }
     3519
     3520        return $success;
     3521}
     3522
     3523/**
     3524 * Delete a membership invitation.
     3525 *
     3526 * @since 8.0.0
     3527 *
     3528 * @param intring $args {
     3529 *     Array of arguments.
     3530 *     @type int|array $id            Id(s) of the invitation(s) to remove.
     3531 *     @type int       $invitee_email Email address of the user being invited.
     3532 *     @type int       $network_id    ID of the network to which the user is being invited.
     3533 *     @type int       $inviter_id    ID of the inviting user.
     3534 *     @type int       $accepted      Whether the invitation has been accepted yet.
     3535 *     @type int       $invite_sent   Whether the invitation has been sent yet.
     3536 * }
     3537 * @return bool True if all were deleted.
     3538 */
     3539function bp_members_invitations_delete_invites( $args ) {
     3540        $r = bp_parse_args( $args, array(
     3541                'id'            => 0,
     3542                'invitee_email' => '',
     3543                'network_id'    => get_current_network_id(),
     3544                'inviter_id'    => null,
     3545                'accepted'      => null,
     3546                'invite_sent'   => null
     3547        ), 'community_invitation_delete_invites' );
     3548
     3549        $inv_args = array(
     3550                'id'            => $r['id'],
     3551                'invitee_email' => $r['invitee_email'],
     3552                'item_id'       => $r['network_id'],
     3553                'inviter_id'    => $r['inviter_id'],
     3554        );
     3555
     3556        // Find the invitation(s).
     3557        $invites       = bp_members_invitations_get_invites( $inv_args );
     3558        $total_count   = count( $invites );
     3559
     3560        // Loop through, deleting each invitation.
     3561        $deleted = 0;
     3562        foreach ( $invites as $invite ) {
     3563                $success = bp_members_invitations_delete_by_id( $invite->id );
     3564                if ( $success ) {
     3565                        $deleted++;
     3566                }
     3567        }
     3568
     3569        return $deleted === $total_count;
     3570}
     3571
     3572/**
     3573 * Get hash based on details of a membership invitation and the inviter.
     3574 *
     3575 * @since 8.0.0
     3576 *
     3577 * @param BP_Invitation object $invitation Invitation to create hash from.
     3578 *
     3579 * @return string $hash Calculated sha1 hash.
     3580 */
     3581function bp_members_invitations_get_hash( BP_Invitation $invitation ) {
     3582        $hash = false;
     3583
     3584        if ( ! empty( $invitation->id ) ) {
     3585                $inviter_ud = get_userdata( $invitation->inviter_id );
     3586                if ( $inviter_ud ) {
     3587                        /*
     3588                         * Use some inviter details as part of the hash so that invitations from
     3589                         * users who are subsequently marked as spam will be invalidated.
     3590                         */
     3591                        $hash = wp_hash( "{$invitation->inviter_id}:{$invitation->invitee_email}:{$inviter_ud->user_status}:{$inviter_ud->user_registered}" );
     3592                }
     3593        }
     3594
     3595        // If there's a problem, return a string that will change and thus fail.
     3596        if ( ! $hash ) {
     3597                $hash = wp_generate_password( 32, false );
     3598        }
     3599
     3600        /**
     3601         * Filters the hash calculated by the invitation details.
     3602         *
     3603         * @since 8.0.0
     3604         *
     3605         * @param string $hash Calculated sha1 hash.
     3606         * @param BP_Invitation object $invitation Invitation hash was created from.
     3607         */
     3608        return apply_filters( 'bp_members_invitations_get_hash', $hash, $invitation );
     3609}
     3610
     3611/**
     3612 * Get the current invitation specified by the $_GET parameters.
     3613 *
     3614 * @since 8.0.0
     3615 *
     3616 * @return BP_Invitation $invite Invitation specified by the $_GET parameters.
     3617 */
     3618function bp_get_members_invitation_from_request() {
     3619        $invites_class = new BP_Members_Invitation_Manager();
     3620        $invite        = $invites_class->get_by_id( 0 );
     3621
     3622        if ( bp_get_members_invitations_allowed() && ! empty( $_GET['inv'] ) ) {
     3623                // Check to make sure the passed hash matches a calculated hash.
     3624                $maybe_invite = $invites_class->get_by_id( absint( $_GET['inv'] ) );
     3625                $hash = bp_members_invitations_get_hash( $maybe_invite );
     3626                if ( $_GET['ih'] === $hash ) {
     3627                        $invite = $maybe_invite;
     3628                }
     3629        }
     3630
     3631        /**
     3632         * Filters the invitation specified by the $_GET parameters.
     3633         *
     3634         * @since 8.0.0
     3635         *
     3636         * @param BP_Invitation $invite Invitation specified by the $_GET parameters.
     3637         */
     3638        return apply_filters( 'bp_get_members_invitation_from_request', $invite );
     3639}
  • new file src/bp-members/bp-members-invitations.php

    diff --git src/bp-members/bp-members-invitations.php src/bp-members/bp-members-invitations.php
    new file mode 100644
    index 000000000..1d7df3a55
    - +  
     1<?php
     2/**
     3 * BuddyPress Membersip Invitations
     4 *
     5 * @package BuddyPress
     6 * @subpackage MembersInvitations
     7 * @since 8.0.0
     8 */
     9
     10// Exit if accessed directly.
     11defined( 'ABSPATH' ) || exit;
     12
     13function bp_members_invitations_setup_nav() {
     14        if ( ! bp_get_members_invitations_allowed() ) {
     15                return;
     16        }
     17
     18        $user_has_access  = bp_user_has_access();
     19        $user_can_send    = bp_user_can( bp_displayed_user_id(), 'bp_members_send_invitation' );
     20        $user_has_invites = bp_members_invitations_user_has_sent_invites();
     21
     22        /* Add 'Invitations' to the main user profile navigation */
     23        bp_core_new_nav_item(
     24                array(
     25                        'name'                    => __( 'Invitations', 'buddypress' ),
     26                        'slug'                    => bp_get_members_invitations_slug(),
     27                        'position'                => 80,
     28                        'screen_function'         => 'members_screen_send_invites',
     29                        'default_subnav_slug'     => 'send-invites',
     30                        'show_for_displayed_user' => $user_has_access && ( $user_can_send || $user_has_invites )
     31                )
     32        );
     33
     34        $parent_link = trailingslashit( bp_displayed_user_domain() . bp_get_members_invitations_slug() );
     35
     36        /* Create two subnav items for community invitations */
     37        bp_core_new_subnav_item(
     38                array(
     39                        'name'            => __( 'Send Invites', 'buddypress' ),
     40                        'slug'            => 'send-invites',
     41                        'parent_slug'     => bp_get_members_invitations_slug(),
     42                        'parent_url'      => $parent_link,
     43                        'screen_function' => 'members_screen_send_invites',
     44                        'position'        => 10,
     45                        'user_has_access' => $user_has_access && $user_can_send && bp_is_my_profile()
     46                )
     47        );
     48
     49        bp_core_new_subnav_item(
     50                array(
     51                        'name'            => __( 'Pending Invites', 'buddypress' ),
     52                        'slug'            => 'list-invites',
     53                        'parent_slug'     => bp_get_members_invitations_slug(),
     54                        'parent_url'      => $parent_link,
     55                        'screen_function' => 'members_screen_list_sent_invites',
     56                        'position'        => 20,
     57                        'user_has_access' => $user_has_access && ( $user_can_send || $user_has_invites )
     58                )
     59        );
     60}
     61add_action( 'bp_setup_nav', 'bp_members_invitations_setup_nav' );
     62
     63/**
     64 * When a user joins the network via an invitation, skip sending the activation email.
     65 *
     66 * @param bool   $send           Whether or not to send the activation key.
     67 * @param int    $user_id        User ID to send activation key to.
     68 * @param string $user_email     User email to send activation key to.
     69 * @param string $activation_key Activation key to be sent.
     70 * @param array  $usermeta       Miscellaneous metadata about the user (blog-specific
     71 *                               signup data, xprofile data, etc).
     72 */
     73function bp_members_invitations_cancel_activation_email( $send, $user_id, $user_email, $activation_key, $usermeta ) {
     74        $invite = bp_members_invitations_get_invites(
     75                array(
     76                        'invitee_email' => $user_email,
     77                        'invite_sent'   => 'sent'
     78                )
     79        );
     80
     81        if ( $invite ) {
     82                $send = false;
     83        }
     84
     85        return $send;
     86}
     87add_filter( 'bp_core_signup_send_activation_key', 'bp_members_invitations_cancel_activation_email', 10, 5 );
     88
     89/**
     90 * When a user joins the network via an invitation:
     91 * - mark all invitations and requests as accepted
     92 * - activate the user upon signup
     93 *
     94 * @param bool|WP_Error   $user_id       True on success, WP_Error on failure.
     95 * @param string          $user_login    Login name requested by the user.
     96 * @param string          $user_password Password requested by the user.
     97 * @param string          $user_email    Email address requested by the user.
     98 */
     99function bp_members_invitations_complete_signup( $user_id, $user_login, $user_password, $user_email ) {
     100        if ( ! $user_id ) {
     101                return;
     102        }
     103
     104        // Check to see if this signup is the result of a valid invitation.
     105        $invite = bp_get_members_invitation_from_request();
     106        if ( ! $invite->id ) {
     107                return;
     108        }
     109
     110        // Add the accepted invitation ID to the user's meta.
     111        bp_update_user_meta( $user_id, 'accepted_members_invitation', $invite->id );
     112
     113        // We will mark all invitations to this user as "accepted."
     114        $invites_class = new BP_Members_Invitation_Manager();
     115        $args          = array(
     116                'invitee_email' => $user_email,
     117                'item_id'       => get_current_network_id(),
     118                'type'          => 'all'
     119        );
     120        $invites_class->mark_accepted( $args );
     121
     122        // User has already verified their email by responding to the invitation, so we can activate.
     123        $key = bp_get_user_meta( $user_id, 'activation_key', true );
     124        if ( $key ) {
     125                /**
     126                 * Filters the activation signup.
     127                 *
     128                 * @since 1.1.0
     129                 *
     130                 * @param bool|int $value Value returned by activation.
     131                 *                        Integer on success, boolean on failure.
     132                 */
     133                $user = apply_filters( 'bp_core_activate_account', bp_core_activate_signup( $key ) );
     134
     135                // If there were errors, add a message and redirect.
     136                if ( ! empty( $user->errors ) ) {
     137                        bp_core_add_message( $user->get_error_message(), 'error' );
     138                        bp_core_redirect( trailingslashit( bp_get_root_domain() . '/' . $bp->pages->activate->slug ) );
     139                }
     140
     141                bp_core_add_message( __( 'Your account is now active!', 'buddypress' ) );
     142                bp_core_redirect( add_query_arg( 'activated', '1', bp_get_activation_page() ) );
     143        }
     144}
     145add_action( 'bp_core_signup_user', 'bp_members_invitations_complete_signup', 10, 4 );
  • src/bp-members/bp-members-template.php

    diff --git src/bp-members/bp-members-template.php src/bp-members/bp-members-template.php
    index 541b27479..117f6fcb6 100644
    function bp_activate_slug() { 
    274274                return apply_filters( 'bp_get_activate_slug', $slug );
    275275        }
    276276
     277/**
     278 * Output the members invitation pane slug.
     279 *
     280 * @since 8.0.0
     281 *
     282 */
     283function bp_members_invitations_slug() {
     284        echo bp_get_members_invitations_slug();
     285}
     286        /**
     287         * Return the members invitations root slug.
     288         *
     289         * @since 8.0.0
     290         *
     291         * @return string
     292         */
     293        function bp_get_members_invitations_slug() {
     294
     295                /**
     296                 * Filters the Members invitations pane root slug.
     297                 *
     298                 * @since 8.0.0
     299                 *
     300                 * @param string $slug Members invitations pane root slug.
     301                 */
     302                return apply_filters( 'bp_get_members_invitations_slug', _x( 'invitations', 'Member profile invitations pane URL base', 'buddypress' ) );
     303        }
     304
    277305/**
    278306 * Initialize the members loop.
    279307 *
    function bp_signup_email_value() { 
    23812409         */
    23822410        function bp_get_signup_email_value() {
    23832411                $value = '';
    2384                 if ( isset( $_POST['signup_email'] ) )
     2412                if ( isset( $_POST['signup_email'] ) ) {
    23852413                        $value = $_POST['signup_email'];
     2414                } else if ( bp_get_members_invitations_allowed() ) {
     2415                        $invite = bp_get_members_invitation_from_request();
     2416                        if ( $invite ) {
     2417                                $value = $invite->invitee_email;
     2418                        }
     2419                }
    23862420
    23872421                /**
    23882422                 * Filters the email address submitted during signup.
    function bp_signup_allowed() { 
    27552789                return apply_filters( 'bp_get_signup_allowed', (bool) bp_get_option( 'users_can_register' ) );
    27562790        }
    27572791
     2792/**
     2793 * Are users allowed to invite users to join this site?
     2794 *
     2795 * @since 8.0.0
     2796 *
     2797 * @return bool
     2798 */
     2799function bp_get_members_invitations_allowed() {
     2800        /**
     2801         * Filters whether or not community invitations are allowed.
     2802         *
     2803         * @since 8.0.0
     2804         *
     2805         * @param bool $allowed Whether or not community invitations are allowed.
     2806         */
     2807        return apply_filters( 'bp_get_members_invitations_allowed', bp_is_active( 'members', 'invitations' ) && (bool) bp_get_option( 'bp-enable-members-invitations' ) );
     2808}
     2809
    27582810/**
    27592811 * Hook member activity feed to <head>.
    27602812 *
    function bp_avatar_delete_link() { 
    28692921                 */
    28702922                return apply_filters( 'bp_get_avatar_delete_link', wp_nonce_url( bp_displayed_user_domain() . bp_get_profile_slug() . '/change-avatar/delete-avatar/', 'bp_delete_avatar_link' ) );
    28712923        }
     2924
     2925
     2926/** The Members Invitations Loop ******************************************************************/
     2927
     2928/**
     2929 * Initialize the community invitations loop.
     2930 *
     2931 * Based on the $args passed, bp_has_invitations() populates
     2932 * buddypress()->invitations->query_loop global, enabling the use of BP
     2933 * templates and template functions to display a list of invitations.
     2934 *
     2935 * @since 8.0.0
     2936 *
     2937 * @param array|string $args {
     2938 *     Arguments for limiting the contents of the invitations loop. Can be
     2939 *     passed as an associative array, or as a URL query string.
     2940 *
     2941 *     See {@link BP_Invitations_Invitation::get()} for detailed
     2942 *     information on the arguments.  In addition, also supports:
     2943 *
     2944 *     @type int    $max      Optional. Max items to display. Default: false.
     2945 *     @type string $page_arg URL argument to use for pagination.
     2946 *                            Default: 'ipage'.
     2947 * }
     2948 * @return bool
     2949 */
     2950function bp_has_members_invitations( $args = '' ) {
     2951
     2952        // Get the user ID.
     2953        if ( bp_displayed_user_id() ) {
     2954                $user_id = bp_displayed_user_id();
     2955        } else {
     2956                $user_id = bp_loggedin_user_id();
     2957        }
     2958
     2959        // Set the search terms (by default an empty string to get all notifications)
     2960        $search_terms = '';
     2961
     2962        if ( isset( $_REQUEST['s'] ) ) {
     2963                $search_terms = stripslashes( $_REQUEST['s'] );
     2964        }
     2965
     2966        // Parse the args.
     2967        $r = bp_parse_args( $args, array(
     2968                'id'                => false,
     2969                'inviter_id'        => $user_id,
     2970                'invitee_email'     => false,
     2971                'item_id'           => false,
     2972                'type'              => 'invite',
     2973                'invite_sent'       => 'all',
     2974                'accepted'          => 'pending',
     2975                'search_terms'      => $search_terms,
     2976                'order_by'          => 'date_modified',
     2977                'sort_order'        => 'DESC',
     2978                'page'              => 1,
     2979                'per_page'          => 25,
     2980                'fields'            => 'all',
     2981
     2982                // These are additional arguments that are not available in
     2983                // BP_Invitations_Invitation::get().
     2984                'page_arg'          => 'ipage',
     2985        ), 'has_community_invitations' );
     2986
     2987        // Get the notifications.
     2988        $query_loop = new BP_Members_Invitations_Template( $r );
     2989
     2990        // Setup the global query loop.
     2991        buddypress()->members->invitations->query_loop = $query_loop;
     2992
     2993        /**
     2994         * Filters whether or not the user has network invitations to display.
     2995         *
     2996         * @since 8.0.0
     2997         *
     2998         * @param bool                      $value      Whether or not there are network invitations to display.
     2999         * @param BP_Notifications_Template $query_loop BP_Members_Invitations_Template object instance.
     3000         * @param array                     $r          Array of arguments passed into the BP_Members_Invitations_Template class.
     3001         */
     3002        return apply_filters( 'bp_has_members_invitations', $query_loop->has_invitations(), $query_loop, $r );
     3003}
     3004
     3005/**
     3006 * Get the network invitations returned by the template loop.
     3007 *
     3008 * @since 8.0.0
     3009 *
     3010 * @return array List of network invitations.
     3011 */
     3012function bp_the_members_invitations() {
     3013        return buddypress()->members->invitations->query_loop->invitations();
     3014}
     3015
     3016/**
     3017 * Get the current network invitation object in the loop.
     3018 *
     3019 * @since 8.0.0
     3020 *
     3021 * @return object The current network invitation within the loop.
     3022 */
     3023function bp_the_members_invitation() {
     3024        return buddypress()->members->invitations->query_loop->the_invitation();
     3025}
     3026
     3027/**
     3028 * Output the pagination count for the current network invitations loop.
     3029 *
     3030 * @since 8.0.0
     3031 */
     3032function bp_members_invitations_pagination_count() {
     3033        echo bp_get_members_invitations_pagination_count();
     3034}
     3035        /**
     3036         * Return the pagination count for the current network invitation loop.
     3037         *
     3038         * @since 8.0.0
     3039         *
     3040         * @return string HTML for the pagination count.
     3041         */
     3042        function bp_get_members_invitations_pagination_count() {
     3043                $query_loop = buddypress()->members->invitations->query_loop;
     3044                $start_num  = intval( ( $query_loop->pag_page - 1 ) * $query_loop->pag_num ) + 1;
     3045                $from_num   = bp_core_number_format( $start_num );
     3046                $to_num     = bp_core_number_format( ( $start_num + ( $query_loop->pag_num - 1 ) > $query_loop->total_invitation_count ) ? $query_loop->total_invitation_count : $start_num + ( $query_loop->pag_num - 1 ) );
     3047                $total      = bp_core_number_format( $query_loop->total_invitation_count );
     3048
     3049                if ( 1 == $query_loop->total_invitation_count ) {
     3050                        $pag = __( 'Viewing 1 invitation', 'buddypress' );
     3051                } else {
     3052                        /* translators: 1: notification from number. 2: notification to number. 3: total notifications. */
     3053                        $pag = sprintf( _n( 'Viewing %1$s - %2$s of %3$s invitation', 'Viewing %1$s - %2$s of %3$s invitations', $query_loop->total_invitation_count, 'buddypress' ), $from_num, $to_num, $total );
     3054                }
     3055
     3056                /**
     3057                 * Filters the pagination count for the current network invitation loop.
     3058                 *
     3059                 * @since 1.9.0
     3060                 *
     3061                 * @param string $pag HTML for the pagination count.
     3062                 */
     3063                return apply_filters( 'bp_get_members_invitations_pagination_count', $pag );
     3064        }
     3065
     3066/**
     3067 * Output the pagination links for the current network invitation loop.
     3068 *
     3069 * @since 8.0.0
     3070 */
     3071function bp_members_invitations_pagination_links() {
     3072        echo bp_get_members_invitations_pagination_links();
     3073}
     3074        /**
     3075         * Return the pagination links for the current network invitations loop.
     3076         *
     3077         * @since 8.0.0
     3078         *
     3079         * @return string HTML for the pagination links.
     3080         */
     3081        function bp_get_members_invitations_pagination_links() {
     3082
     3083                /**
     3084                 * Filters the pagination links for the current network invitations loop.
     3085                 *
     3086                 * @since 8.0.0
     3087                 *
     3088                 * @param string $pag_links HTML for the pagination links.
     3089                 */
     3090                return apply_filters( 'bp_get_members_invitations_pagination_links', buddypress()->members->invitations->query_loop->pag_links );
     3091        }
     3092
     3093/**
     3094 * Output the requested property of the invitation currently being iterated on.
     3095 *
     3096 * @since 8.0.0
     3097 *
     3098 * @param string $property The name of the property to display.
     3099 * @param string $context  The context of display.
     3100 *                         Possible values are 'attribute' and 'html'.
     3101 */
     3102function bp_the_members_invitation_property( $property = '', $context = 'html' ) {
     3103        if ( ! $property ) {
     3104                return;
     3105        }
     3106
     3107        /**
     3108         * Use this filter to sanitize the output.
     3109         *
     3110         * @since 8.0.0
     3111         *
     3112         * @param int|string $value    The value for the requested property.
     3113         * @param string     $property The name of the requested property.
     3114         * @param string     $context  The context of display.
     3115         */
     3116        echo apply_filters( 'bp_the_members_invitation_property', bp_get_the_members_invitation_property( $property ), $property, $context );
     3117}
     3118        /**
     3119         * Return the value for a property of the network invitation currently being iterated on.
     3120         *
     3121         * @since 8.0.0
     3122         *
     3123         * @return int ID of the current network invitation.
     3124         */
     3125        function bp_get_the_members_invitation_property( $property = 'id' ) {
     3126
     3127                switch ( $property ) {
     3128                        case 'id':
     3129                        case 'user_id':
     3130                        case 'item_id':
     3131                        case 'secondary_item_id':
     3132                        case 'invite_sent':
     3133                        case 'accepted':
     3134                                $value = 0;
     3135                                break;
     3136                        case 'invitee_email':
     3137                        case 'type':
     3138                        case 'content':
     3139                        case 'date_modified':
     3140                                $value = '';
     3141                                break;
     3142                        default:
     3143                                // A known property has not been specified.
     3144                                $property = null;
     3145                                $value = '';
     3146                                break;
     3147                }
     3148
     3149                if ( isset( buddypress()->members->invitations->query_loop->invitation->{$property} ) ) {
     3150                        $value = buddypress()->members->invitations->query_loop->invitation->{$property};
     3151                }
     3152
     3153                /**
     3154                 * Filters the property of the network invitation currently being iterated on.
     3155                 *
     3156                 * @since 8.0.0
     3157                 *
     3158                 * @param int|string $value Property value of the network invitation being iterated on.
     3159                 */
     3160                return apply_filters( 'bp_get_the_members_invitation_property_' . $property, $value );
     3161        }
     3162
     3163/**
     3164 * Output the action links for the current invitation.
     3165 *
     3166 * @since 8.0.0
     3167 *
     3168 * @param array|string $args Array of arguments.
     3169 */
     3170function bp_the_members_invitation_action_links( $args = '' ) {
     3171        echo bp_get_the_members_invitation_action_links( $args );
     3172}
     3173        /**
     3174         * Return the action links for the current invitation.
     3175         *
     3176         * @since 8.0.0
     3177         *
     3178         * @param array|string $args {
     3179         *     @type string $before  HTML before the links.
     3180         *     @type string $after   HTML after the links.
     3181         *     @type string $sep     HTML between the links.
     3182         *     @type array  $links   Array of links to implode by 'sep'.
     3183         *     @type int    $user_id User ID to fetch action links for. Defaults to displayed user ID.
     3184         * }
     3185         * @return string HTML links for actions to take on single notifications.
     3186         */
     3187        function bp_get_the_members_invitation_action_links( $args = '' ) {
     3188                // Set default user ID to use.
     3189                $inviter_id = isset( $args['inviter_id'] ) ? $args['inviter_id'] : bp_displayed_user_id();
     3190
     3191                // Parse.
     3192                $r = wp_parse_args( $args, array(
     3193                        'before' => '',
     3194                        'after'  => '',
     3195                        'sep'    => ' | ',
     3196                        'links'  => array(
     3197                                bp_get_the_members_invitation_resend_link( $inviter_id ),
     3198                                bp_get_the_members_invitation_delete_link( $inviter_id )
     3199                        )
     3200                ) );
     3201
     3202                // Build the links.
     3203                $retval = $r['before'] . implode( $r['sep'], $r['links'] ) . $r['after'];
     3204
     3205                /**
     3206                 * Filters the action links for the current notification.
     3207                 *
     3208                 * @since 1.9.0
     3209                 * @since 2.6.0 Added the `$r` parameter.
     3210                 *
     3211                 * @param string $retval HTML links for actions to take on single notifications.
     3212                 * @param array  $r      Array of parsed arguments.
     3213                 */
     3214                return apply_filters( 'bp_get_the_members_invitation_action_links', $retval, $r );
     3215        }
     3216
     3217/**
     3218 * Output the resend link for the current invitation.
     3219 *
     3220 * @since 8.0.0
     3221 *
     3222 * @param int $user_id The user ID.
     3223 */
     3224function bp_the_members_invitations_resend_link( $user_id = 0 ) {
     3225        echo bp_get_the_members_invitation_delete_link( $user_id );
     3226}
     3227        /**
     3228         * Return the resend link for the current notification.
     3229         *
     3230         * @since 78.0.0
     3231         *
     3232         * @param int $user_id The user ID.
     3233         * @return string
     3234         */
     3235        function bp_get_the_members_invitation_resend_link( $user_id = 0 ) {
     3236                // Set default user ID to use.
     3237                $user_id = 0 === $user_id ? bp_displayed_user_id() : $user_id;
     3238
     3239                // Don't allow resending of accepted invitations.
     3240                if ( bp_get_the_members_invitation_property( 'accepted' ) ) {
     3241                        return;
     3242                }
     3243
     3244                $retval = sprintf( '<a href="%1$s" class="resend secondary confirm bp-tooltip">%2$s</a>', esc_url( bp_get_the_members_invitations_resend_url( $user_id ) ), __( 'Resend', 'buddypress' ) );
     3245
     3246                /**
     3247                 * Filters the resend link for the current invitation.
     3248                 *
     3249                 * @since 8.0.0
     3250                 *
     3251                 * @param string $retval  HTML for the delete link for the current notification.
     3252                 * @param int    $user_id The user ID.
     3253                 */
     3254                return apply_filters( 'bp_get_the_members_invitation_resend_link', $retval, $user_id );
     3255        }
     3256
     3257/**
     3258 * Output the URL used for resending a single invitation.
     3259 *
     3260 * Since this function directly outputs a URL, it is escaped.
     3261 *
     3262 * @since 8.0.0
     3263 *
     3264 * @param int $user_id The user ID.
     3265 */
     3266function bp_the_members_invitations_resend_url( $user_id = 0 ) {
     3267        echo esc_url( bp_get_the_members_invitations_resend_url( $user_id ) );
     3268}
     3269        /**
     3270         * Return the URL used for resending a single invitation.
     3271         *
     3272         * @since 8.0.0
     3273         *
     3274         * @param int $user_id The user ID.
     3275         * @return string
     3276         */
     3277        function bp_get_the_members_invitations_resend_url( $user_id = 0 ) {
     3278                // Set default user ID to use.
     3279                $user_id = 0 === $user_id ? bp_displayed_user_id() : $user_id;
     3280                $link = bp_get_members_invitations_list_invites_permalink( $user_id );
     3281
     3282                // Get the ID.
     3283                $id = bp_get_the_members_invitation_property( 'id' );
     3284
     3285                // Get the args to add to the URL.
     3286                $args = array(
     3287                        'action'        => 'resend',
     3288                        'invitation_id' => $id
     3289                );
     3290
     3291                // Add the args.
     3292                $url = add_query_arg( $args, $link );
     3293
     3294                // Add the nonce.
     3295                $url = wp_nonce_url( $url, 'bp_network_invitation_resend_' . $id );
     3296
     3297                /**
     3298                 * Filters the URL used for resending a single invitation.
     3299                 *
     3300                 * @since 8.0.0
     3301                 *
     3302                 * @param string $url     URL used for deleting a single invitation.
     3303                 * @param int    $user_id The user ID.
     3304                 */
     3305                return apply_filters( 'bp_get_the_members_invitations_resend_url', $url, $user_id );
     3306        }
     3307
     3308/**
     3309 * Output the delete link for the current invitation.
     3310 *
     3311 * @since 8.0.0
     3312 *
     3313 * @param int $user_id The user ID.
     3314 */
     3315function bp_the_members_invitations_delete_link( $user_id = 0 ) {
     3316        echo bp_get_the_members_invitation_delete_link( $user_id );
     3317}
     3318        /**
     3319         * Return the delete link for the current invitation.
     3320         *
     3321         * @since 8.0.0
     3322         *
     3323         * @param int $user_id The user ID.
     3324         * @return string
     3325         */
     3326        function bp_get_the_members_invitation_delete_link( $user_id = 0 ) {
     3327                // Set default user ID to use.
     3328                $user_id = 0 === $user_id ? bp_displayed_user_id() : $user_id;
     3329
     3330                // Modify the message for accepted/not accepted invitatons.
     3331                if ( bp_get_the_members_invitation_property( 'accepted' ) ) {
     3332                        $message = __( 'Delete', 'buddypress' );
     3333                } else {
     3334                        $message = __( 'Cancel', 'buddypress' );
     3335                }
     3336
     3337                $retval = sprintf( '<a href="%1$s" class="delete secondary confirm bp-tooltip">%2$s</a>', esc_url( bp_get_the_members_invitations_delete_url( $user_id ) ), $message );
     3338
     3339                /**
     3340                 * Filters the delete link for the current invitation.
     3341                 *
     3342                 * @since 8.0.0
     3343                 *
     3344                 * @param string $retval  HTML for the delete link for the current notification.
     3345                 * @param int    $user_id The user ID.
     3346                 */
     3347                return apply_filters( 'bp_get_the_members_invitation_delete_link', $retval, $user_id );
     3348        }
     3349
     3350/**
     3351 * Output the URL used for deleting a single invitation.
     3352 *
     3353 * Since this function directly outputs a URL, it is escaped.
     3354 *
     3355 * @since 8.0.0
     3356 *
     3357 * @param int $user_id The user ID.
     3358 */
     3359function bp_the_members_invitations_delete_url( $user_id = 0 ) {
     3360        echo esc_url( bp_get_the_members_invitations_delete_url( $user_id ) );
     3361}
     3362        /**
     3363         * Return the URL used for deleting a single invitation.
     3364         *
     3365         * @since 8.0.0
     3366         *
     3367         * @param int $user_id The user ID.
     3368         * @return string
     3369         */
     3370        function bp_get_the_members_invitations_delete_url( $user_id = 0 ) {
     3371                // Set default user ID to use.
     3372                $user_id = 0 === $user_id ? bp_displayed_user_id() : $user_id;
     3373                $link = bp_get_members_invitations_list_invites_permalink( $user_id );
     3374
     3375                // Get the ID.
     3376                $id = bp_get_the_members_invitation_property( 'id' );
     3377
     3378                // Get the args to add to the URL.
     3379                $args = array(
     3380                        'action'        => 'cancel',
     3381                        'invitation_id' => $id
     3382                );
     3383
     3384                // Add the args.
     3385                $url = add_query_arg( $args, $link );
     3386
     3387                // Add the nonce.
     3388                $url = wp_nonce_url( $url, 'bp_members_invitations_cancel_' . $id );
     3389
     3390                /**
     3391                 * Filters the URL used for deleting a single invitation.
     3392                 *
     3393                 * @since 8.0.0
     3394                 *
     3395                 * @param string $url     URL used for deleting a single invitation.
     3396                 * @param int    $user_id The user ID.
     3397                 */
     3398                return apply_filters( 'bp_get_the_members_invitations_delete_url', $url, $user_id );
     3399        }
     3400
     3401/**
     3402 * Output the members invitations list permalink for a user.
     3403 *
     3404 * @since 8.0.0
     3405 *
     3406 * @param int $user_id The user ID.
     3407 */
     3408function bp_members_invitations_list_invites_permalink( $user_id = 0 ) {
     3409        echo bp_get_members_invitations_list_invites_permalink( $user_id );
     3410}
     3411        /**
     3412         * Return the members invitations list permalink for a user.
     3413         *
     3414         * @since 8.0.0
     3415         *
     3416         * @return string Members invitations list permalink for a user.
     3417         */
     3418        function bp_get_members_invitations_list_invites_permalink( $user_id = 0 ) {
     3419                if ( 0 === $user_id ) {
     3420                        $user_id = bp_loggedin_user_id();
     3421                        $domain  = bp_loggedin_user_domain();
     3422                } else {
     3423                        $domain = bp_core_get_user_domain( (int) $user_id );
     3424                }
     3425
     3426                $retval = trailingslashit( $domain . bp_get_members_invitations_slug() . '/list-invites' );
     3427
     3428                /**
     3429                 * Filters the members invitations list permalink for a user.
     3430                 *
     3431                 * @since 8.0.0
     3432                 *
     3433                 * @param string $retval  Permalink for the sent invitation list screen.
     3434                 * @param int    $user_id The user ID.
     3435                 */
     3436                return apply_filters( 'bp_get_members_invitations_list_invites_permalink', $retval, $user_id );
     3437        }
     3438
     3439/**
     3440 * Output the send invitation permalink for a user.
     3441 *
     3442 * @since 8.0.0
     3443 *
     3444 * @param int $user_id The user ID.
     3445 */
     3446function bp_members_invitations_send_invites_permalink( $user_id = 0 ) {
     3447        echo bp_get_members_invitations_send_invites_permalink( $user_id );
     3448}
     3449        /**
     3450         * Return the send invitations permalink.
     3451         *
     3452         * @since 8.0.0
     3453         *
     3454         * @return string Read notifications permalink.
     3455         */
     3456        function bp_get_members_invitations_send_invites_permalink( $user_id = 0 ) {
     3457                if ( 0 === $user_id ) {
     3458                        $user_id = bp_loggedin_user_id();
     3459                        $domain  = bp_loggedin_user_domain();
     3460                } else {
     3461                        $domain = bp_core_get_user_domain( (int) $user_id );
     3462                }
     3463
     3464                $retval = trailingslashit( $domain . bp_get_members_invitations_slug() . '/send-invites' );
     3465
     3466                /**
     3467                 * Filters the read notifications permalink.
     3468                 *
     3469                 * @since 8.0.0
     3470                 *
     3471                 * @param string $retval  Permalink for the sent invitation list screen.
     3472                 * @param int    $user_id The user ID.
     3473                 */
     3474                return apply_filters( 'bp_get_members_invitations_send_invites_permalink', $retval, $user_id );
     3475        }
  • 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 64903c107..3294a7765 100644
    class BP_Members_Admin { 
    140140                $this->users_url    = bp_get_admin_url( 'users.php' );
    141141                $this->users_screen = bp_core_do_network_admin() ? 'users-network' : 'users';
    142142
     143                $this->members_invites_page = '';
     144
    143145                // Specific config: BuddyPress is not network activated.
    144146                $this->subsite_activated = (bool) is_multisite() && ! bp_is_network_activated();
    145147
    class BP_Members_Admin { 
    219221                        // Registration is turned on.
    220222                        add_action( 'update_site_option_registration',  array( $this, 'multisite_registration_on' ),   10, 2 );
    221223                        add_action( 'update_option_users_can_register', array( $this, 'single_site_registration_on' ), 10, 2 );
     224
     225                        // Member invitations are enabled.
     226                        if ( bp_is_network_activated() ) {
     227                                add_action( 'update_site_option_bp-enable-members-invitations',  array( $this, 'multisite_registration_on' ),   10, 2 );
     228                        } else {
     229                                add_action( 'update_option_bp-enable-members-invitations', array( $this, 'single_site_registration_on' ), 10, 2 );
     230                        }
    222231                }
    223232
    224233                /** Users List - Members Types ***************************************
    class BP_Members_Admin { 
    248257         * @param string $value
    249258         */
    250259        public function multisite_registration_on( $option_name, $value ) {
    251                 if ( 'user' === $value || 'all' === $value ) {
     260                // Is registration enabled or are network invitations enabled?
     261                if ( ( 'user' === $value || 'all' === $value )
     262                        || bp_get_members_invitations_allowed() ) {
    252263                        bp_core_add_page_mappings( array(
    253264                                'register' => 1,
    254265                                'activate' => 1
    class BP_Members_Admin { 
    266277         */
    267278        public function single_site_registration_on( $old_value, $value ) {
    268279                // Single site.
    269                 if ( ! is_multisite() && ! empty( $value ) ) {
     280                if ( ! is_multisite() && ( ! empty( $value ) || bp_get_members_invitations_allowed() ) ) {
    270281                        bp_core_add_page_mappings( array(
    271282                                'register' => 1,
    272283                                'activate' => 1
    class BP_Members_Admin { 
    490501                        );
    491502                }
    492503
     504                // For consistency with non-Multisite, we add a Tools menu in
     505                // the Network Admin as a home for our Tools panel.
     506                if ( is_multisite() && bp_core_do_network_admin() ) {
     507                        $tools_parent = 'network-tools';
     508                } else {
     509                        $tools_parent = 'tools.php';
     510                }
     511
     512                $hooks['members_invitations'] = $this->members_invites_page = add_submenu_page(
     513                        $tools_parent,
     514                        __( 'Manage Invitations',  'buddypress' ),
     515                        __( 'Manage Invitations',  'buddypress' ),
     516                        $this->capability,
     517                        'bp-members-invitations',
     518                        array( $this, 'invitations_admin' )
     519                );
     520
    493521                $edit_page         = 'user-edit';
    494522                $profile_page      = 'profile';
    495523                $this->users_page  = 'users';
    class BP_Members_Admin { 
    510538                        $this->users_page   .= '-network';
    511539                        $this->signups_page .= '-network';
    512540
    513                         $this->members_optouts_page .= '-network';
     541                        $this->members_invites_page .= '-network';
    514542                }
    515543
    516544                // Setup the screen ID's.
    class BP_Members_Admin { 
    598626        public function admin_head() {
    599627                remove_submenu_page( 'users.php',   'bp-profile-edit' );
    600628                remove_submenu_page( 'profile.php', 'bp-profile-edit' );
     629
     630                // Manage Invitations Tool screen is a tab of BP Tools.
     631                if ( is_network_admin() ) {
     632                        remove_submenu_page( 'network-tools', 'bp-members-invitations' );
     633                } else {
     634                        remove_submenu_page( 'tools.php', 'bp-members-invitations' );
     635                }
    601636        }
    602637
    603638        /** Community Profile *****************************************************/
    class BP_Members_Admin { 
    25662601
    25672602                return $value;
    25682603        }
     2604
     2605        /**
     2606         * Set up the signups admin page.
     2607         *
     2608         * Loaded before the page is rendered, this function does all initial
     2609         * setup, including: processing form requests, registering contextual
     2610         * help, and setting up screen options.
     2611         *
     2612         * @since 8.0.0
     2613         *
     2614         * @global $bp_members_invitations_list_table
     2615         */
     2616        public function members_invitations_admin_load() {
     2617                global $bp_members_invitations_list_table;
     2618
     2619                // Build redirection URL.
     2620                $redirect_to = remove_query_arg( array( 'action', 'error', 'updated', 'activated', 'notactivated', 'deleted', 'notdeleted', 'resent', 'notresent', 'do_delete', 'do_resend', 'do_activate', '_wpnonce', 'signup_ids' ), $_SERVER['REQUEST_URI'] );
     2621                $doaction    = bp_admin_list_table_current_bulk_action();
     2622
     2623                /**
     2624                 * Fires at the start of the member invitations admin load.
     2625                 *
     2626                 * @since 8.0.0
     2627                 *
     2628                 * @param string $doaction Current bulk action being processed.
     2629                 * @param array  $_REQUEST Current $_REQUEST global.
     2630                 */
     2631                do_action( 'bp_members_invitations_admin_load', $doaction, $_REQUEST );
     2632
     2633                /**
     2634                 * Filters the allowed actions for use in the user signups admin page.
     2635                 *
     2636                 * @since 8.0.0
     2637                 *
     2638                 * @param array $value Array of allowed actions to use.
     2639                 */
     2640                $allowed_actions = apply_filters( 'bp_members_invitations_admin_allowed_actions', array( 'do_delete',  'do_resend' ) );
     2641
     2642                // Prepare the display of the bulk invitation action screen.
     2643                if ( ! in_array( $doaction, $allowed_actions ) ) {
     2644
     2645                        $bp_members_invitations_list_table = self::get_list_table_class( 'BP_Members_Invitations_List_Table', 'users' );
     2646
     2647                        // The per_page screen option.
     2648                        add_screen_option( 'per_page', array( 'label' => _x( 'Members Invitations', 'Members Invitations per page (screen options)', 'buddypress' ) ) );
     2649
     2650                        get_current_screen()->add_help_tab( array(
     2651                                'id'      => 'bp-members-invitations-overview',
     2652                                'title'   => __( 'Overview', 'buddypress' ),
     2653                                'content' =>
     2654                                '<p>' . __( 'This is the administration screen for member invitations on your site.', 'buddypress' ) . '</p>' .
     2655                                '<p>' . __( 'From the screen options, you can customize the displayed columns and the pagination of this screen.', 'buddypress' ) . '</p>' .
     2656                                '<p>' . __( 'You can reorder the list of invitations by clicking on the Invitee, Inviter, Date Modified, Email Sent, or Accepted column headers.', 'buddypress' ) . '</p>' .
     2657                                '<p>' . __( 'Using the search form, you can find specific invitations more easily. The Invitee Email field will be included in the search.', 'buddypress' ) . '</p>'
     2658                        ) );
     2659
     2660                        get_current_screen()->add_help_tab( array(
     2661                                'id'      => 'bp-members-invitations-actions',
     2662                                'title'   => __( 'Actions', 'buddypress' ),
     2663                                'content' =>
     2664                                '<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>' .
     2665                                '<ul><li>' . __( '"Send" or "Resend" takes you to the confirmation screen before being able to send or resend the invitation email to the desired pending invitee.', 'buddypress' ) . '</li>' .
     2666                                '<li>' . __( '"Delete" allows you to delete an unsent or accepted invitation from your site; "Cancel" allows you to cancel a sent, but not yet accepted, invitation. You will be asked to confirm this deletion.', 'buddypress' ) . '</li></ul>' .
     2667                                '<p>' . __( 'Bulk actions allow you to perform these actions for the selected rows.', 'buddypress' ) . '</p>'
     2668                        ) );
     2669
     2670                        // Help panel - sidebar links.
     2671                        get_current_screen()->set_help_sidebar(
     2672                                '<p><strong>' . __( 'For more information:', 'buddypress' ) . '</strong></p>' .
     2673                                '<p>' . __( '<a href="https://buddypress.org/support/">Support Forums</a>', 'buddypress' ) . '</p>'
     2674                        );
     2675
     2676                        // Add accessible hidden headings and text for the Pending Users screen.
     2677                        get_current_screen()->set_screen_reader_content( array(
     2678                                /* translators: accessibility text */
     2679                                'heading_views'      => __( 'Filter invitations list', 'buddypress' ),
     2680                                /* translators: accessibility text */
     2681                                'heading_pagination' => __( 'Invitation list navigation', 'buddypress' ),
     2682                                /* translators: accessibility text */
     2683                                'heading_list'       => __( 'Invitations list', 'buddypress' ),
     2684                        ) );
     2685
     2686                } else {
     2687                        if ( empty( $_REQUEST['invite_ids' ] ) ) {
     2688                                return;
     2689                        }
     2690                        $invite_ids = wp_parse_id_list( $_REQUEST['invite_ids' ] );
     2691
     2692                        // Handle resent invitations.
     2693                        if ( 'do_resend' == $doaction ) {
     2694
     2695                                // Nonce check.
     2696                                check_admin_referer( 'invitations_resend' );
     2697
     2698                                $success = 0;
     2699                                foreach ( $invite_ids as $invite_id ) {
     2700                                        if ( bp_members_invitation_resend_by_id( $invite_id ) ) {
     2701                                                $success++;
     2702                                        }
     2703                                }
     2704
     2705                                $query_arg = array( 'updated' => 'resent' );
     2706
     2707                                if ( ! empty( $success ) ) {
     2708                                        $query_arg['resent'] = $success;
     2709                                }
     2710
     2711                                $not_sent = count( $invite_ids ) - $success;
     2712                                if ( $not_sent > 0 ) {
     2713                                        $query_arg['notsent'] = $not_sent;
     2714                                }
     2715
     2716                                $redirect_to = add_query_arg( $query_arg, $redirect_to );
     2717
     2718                                bp_core_redirect( $redirect_to );
     2719
     2720                        // Handle invitation deletion.
     2721                        } elseif ( 'do_delete' == $doaction ) {
     2722
     2723                                // Nonce check.
     2724                                check_admin_referer( 'invitations_delete' );
     2725
     2726                                $success = 0;
     2727                                foreach ( $invite_ids as $invite_id ) {
     2728                                        if ( bp_members_invitations_delete_by_id( $invite_id ) ) {
     2729                                                $success++;
     2730                                        }
     2731                                }
     2732
     2733                                $query_arg = array( 'updated' => 'deleted' );
     2734
     2735                                if ( ! empty( $success ) ) {
     2736                                        $query_arg['deleted'] = $success;
     2737                                }
     2738
     2739                                $notdeleted = count( $invite_ids ) - $success;
     2740                                if ( $notdeleted > 0 ) {
     2741                                        $query_arg['notdeleted'] = $notdeleted;
     2742                                }
     2743
     2744                                $redirect_to = add_query_arg( $query_arg, $redirect_to );
     2745
     2746                                bp_core_redirect( $redirect_to );
     2747
     2748                        // Plugins can update other stuff from here.
     2749                        } else {
     2750                                $this->redirect = $redirect_to;
     2751
     2752                                /**
     2753                                 * Fires at end of member invitations admin load
     2754                                 * if doaction does not match any actions.
     2755                                 *
     2756                                 * @since 8.0.0
     2757                                 *
     2758                                 * @param string $doaction Current bulk action being processed.
     2759                                 * @param array  $_REQUEST Current $_REQUEST global.
     2760                                 * @param string $redirect Determined redirect url to send user to.
     2761                                 */
     2762                                do_action( 'bp_members_admin_update_invitations', $doaction, $_REQUEST, $this->redirect );
     2763
     2764                                bp_core_redirect( $this->redirect );
     2765                        }
     2766                }
     2767        }
     2768
     2769        /**
     2770         * Get admin notice when viewing the invitations management page.
     2771         *
     2772         * @since 8.0.0
     2773         *
     2774         * @return array
     2775         */
     2776        private function get_members_invitations_notice() {
     2777
     2778                // Setup empty notice for return value.
     2779                $notice = array();
     2780
     2781                // Updates.
     2782                if ( ! empty( $_REQUEST['updated'] ) ) {
     2783                        switch ( $_REQUEST['updated'] ) {
     2784                                case 'resent':
     2785                                        $notice = array(
     2786                                                'class'   => 'updated',
     2787                                                'message' => ''
     2788                                        );
     2789
     2790                                        if ( ! empty( $_REQUEST['resent'] ) ) {
     2791                                                $notice['message'] .= sprintf(
     2792                                                        /* translators: %s: number of invitation emails sent */
     2793                                                        _nx( '%s invtitation email successfully sent! ', '%s invitation emails successfully sent! ',
     2794                                                         absint( $_REQUEST['resent'] ),
     2795                                                         'members invitation resent',
     2796                                                         'buddypress'
     2797                                                        ),
     2798                                                        number_format_i18n( absint( $_REQUEST['resent'] ) )
     2799                                                );
     2800                                        }
     2801
     2802                                        if ( ! empty( $_REQUEST['notsent'] ) ) {
     2803                                                $notice['message'] .= sprintf(
     2804                                                        /* translators: %s: number of unsent invitation emails */
     2805                                                        _nx( '%s invitation email was not sent.', '%s invitation emails were not sent.',
     2806                                                         absint( $_REQUEST['notsent'] ),
     2807                                                         'members invitation notsent',
     2808                                                         'buddypress'
     2809                                                        ),
     2810                                                        number_format_i18n( absint( $_REQUEST['notsent'] ) )
     2811                                                );
     2812
     2813                                                if ( empty( $_REQUEST['resent'] ) ) {
     2814                                                        $notice['class'] = 'error';
     2815                                                }
     2816                                        }
     2817
     2818                                        break;
     2819
     2820                                case 'deleted':
     2821                                        $notice = array(
     2822                                                'class'   => 'updated',
     2823                                                'message' => ''
     2824                                        );
     2825
     2826                                        if ( ! empty( $_REQUEST['deleted'] ) ) {
     2827                                                $notice['message'] .= sprintf(
     2828                                                        /* translators: %s: number of deleted invitations */
     2829                                                        _nx( '%s invitation successfully deleted!', '%s invitations successfully deleted!',
     2830                                                         absint( $_REQUEST['deleted'] ),
     2831                                                         'members invitation deleted',
     2832                                                         'buddypress'
     2833                                                        ),
     2834                                                        number_format_i18n( absint( $_REQUEST['deleted'] ) )
     2835                                                );
     2836                                        }
     2837
     2838                                        if ( ! empty( $_REQUEST['notdeleted'] ) ) {
     2839                                                $notice['message'] .= sprintf(
     2840                                                        /* translators: %s: number of invitations that failed to be deleted */
     2841                                                        _nx( '%s invitation was not deleted.', '%s invitations were not deleted.',
     2842                                                         absint( $_REQUEST['notdeleted'] ),
     2843                                                         'members invitation notdeleted',
     2844                                                         'buddypress'
     2845                                                        ),
     2846                                                        number_format_i18n( absint( $_REQUEST['notdeleted'] ) )
     2847                                                );
     2848
     2849                                                if ( empty( $_REQUEST['deleted'] ) ) {
     2850                                                        $notice['class'] = 'error';
     2851                                                }
     2852                                        }
     2853
     2854                                        break;
     2855                        }
     2856                }
     2857
     2858                // Errors.
     2859                if ( ! empty( $_REQUEST['error'] ) ) {
     2860                        switch ( $_REQUEST['error'] ) {
     2861                                case 'do_resend':
     2862                                        $notice = array(
     2863                                                'class'   => 'error',
     2864                                                'message' => esc_html__( 'There was a problem sending the invitation emails. Please try again.', 'buddypress' ),
     2865                                        );
     2866                                        break;
     2867
     2868                                case 'do_delete':
     2869                                        $notice = array(
     2870                                                'class'   => 'error',
     2871                                                'message' => esc_html__( 'There was a problem deleting invitations. Please try again.', 'buddypress' ),
     2872                                        );
     2873                                        break;
     2874                        }
     2875                }
     2876
     2877                return $notice;
     2878        }
     2879
     2880        /**
     2881         * Member invitations admin page router.
     2882         *
     2883         * Depending on the context, display
     2884         * - the list of invitations,
     2885         * - or the delete confirmation screen,
     2886         * - or the "resend" email confirmation screen.
     2887         *
     2888         * Also prepare the admin notices.
     2889         *
     2890         * @since 8.0.0
     2891         */
     2892        public function invitations_admin() {
     2893                $doaction = bp_admin_list_table_current_bulk_action();
     2894
     2895                // Prepare notices for admin.
     2896                $notice = $this->get_members_invitations_notice();
     2897
     2898                // Display notices.
     2899                if ( ! empty( $notice ) ) :
     2900                        if ( 'updated' === $notice['class'] ) : ?>
     2901
     2902                                <div id="message" class="<?php echo esc_attr( $notice['class'] ); ?> notice is-dismissible">
     2903
     2904                        <?php else: ?>
     2905
     2906                                <div class="<?php echo esc_attr( $notice['class'] ); ?> notice is-dismissible">
     2907
     2908                        <?php endif; ?>
     2909
     2910                                <p><?php echo $notice['message']; ?></p>
     2911                        </div>
     2912
     2913                <?php endif;
     2914
     2915                // Show the proper screen.
     2916                switch ( $doaction ) {
     2917                        case 'delete' :
     2918                        case 'resend' :
     2919                                $this->invitations_admin_manage( $doaction );
     2920                                break;
     2921
     2922                        default:
     2923                                $this->invitations_admin_index();
     2924                                break;
     2925                }
     2926        }
     2927
     2928        /**
     2929         * This is the list of invitations.
     2930         *
     2931         * @since 8.0.0
     2932         *
     2933         * @global $plugin_page
     2934         * @global $bp_members_invitations_list_table
     2935         */
     2936        public function invitations_admin_index() {
     2937                global $plugin_page, $bp_members_invitations_list_table;
     2938
     2939                $usersearch = ! empty( $_REQUEST['s'] ) ? stripslashes( $_REQUEST['s'] ) : '';
     2940
     2941                // Prepare the group items for display.
     2942                $bp_members_invitations_list_table->prepare_items();
     2943
     2944                if ( is_network_admin() ) {
     2945                        $form_url = network_admin_url( 'admin.php' );
     2946                } else {
     2947                        $form_url = bp_get_admin_url( 'tools.php' );
     2948                }
     2949
     2950                $form_url = add_query_arg(
     2951                        array(
     2952                                'page' => 'bp-members-invitations',
     2953                        ),
     2954                        $form_url
     2955                );
     2956
     2957                $search_form_url = remove_query_arg(
     2958                        array(
     2959                                'action',
     2960                                'deleted',
     2961                                'notdeleted',
     2962                                'error',
     2963                                'updated',
     2964                                'delete',
     2965                                'activate',
     2966                                'activated',
     2967                                'notactivated',
     2968                                'resend',
     2969                                'resent',
     2970                                'notresent',
     2971                                'do_delete',
     2972                                'do_activate',
     2973                                'do_resend',
     2974                                'action2',
     2975                                '_wpnonce',
     2976                                'invite_ids'
     2977                        ), $_SERVER['REQUEST_URI']
     2978                );
     2979
     2980                ?>
     2981
     2982                <div class="wrap">
     2983                        <h1 class="wp-heading-inline"><?php esc_html_e( 'BuddyPress tools', 'buddypress' ); ?></h1>
     2984                        <hr class="wp-header-end">
     2985
     2986                        <h2 class="nav-tab-wrapper"><?php bp_core_admin_tabs( __( 'Manage Invitations', 'buddypress' ), 'tools' ); ?></h2>
     2987
     2988                        <?php
     2989                        if ( $usersearch ) {
     2990                                printf( '<span class="subtitle">' . __( 'Search results for &#8220;%s&#8221;', 'buddypress' ) . '</span>', esc_html( $usersearch ) );
     2991                        }
     2992                        ?>
     2993
     2994                        <hr class="wp-header-end">
     2995
     2996                        <?php // Display each invitation on its own row. ?>
     2997                        <?php $bp_members_invitations_list_table->views(); ?>
     2998
     2999                        <form id="bp-members-invitations-search-form" action="<?php echo esc_url( $search_form_url ) ;?>">
     3000                                <input type="hidden" name="page" value="<?php echo esc_attr( $plugin_page ); ?>" />
     3001                                <?php $bp_members_invitations_list_table->search_box( __( 'Search Invitations', 'buddypress' ), 'bp-members-invitations' ); ?>
     3002                        </form>
     3003
     3004                        <form id="bp-members-invitations-form" action="<?php echo esc_url( $form_url );?>" method="post">
     3005                                <?php $bp_members_invitations_list_table->display(); ?>
     3006                        </form>
     3007                </div>
     3008        <?php
     3009        }
     3010
     3011        /**
     3012         * This is the confirmation screen for actions.
     3013         *
     3014         * @since 2.0.0
     3015         *
     3016         * @param string $action Delete or resend invitation.
     3017         *
     3018         * @return null|false
     3019         */
     3020        public function invitations_admin_manage( $action = '' ) {
     3021                if ( ! current_user_can( $this->capability ) || empty( $action ) ) {
     3022                        die( '-1' );
     3023                }
     3024
     3025                // Get the IDs from the URL.
     3026                $ids = false;
     3027                if ( ! empty( $_POST['invite_ids'] ) ) {
     3028                        $ids = wp_parse_id_list( $_POST['invite_ids'] );
     3029                } elseif ( ! empty( $_GET['invite_id'] ) ) {
     3030                        $ids = absint( $_GET['invite_id'] );
     3031                }
     3032
     3033
     3034                if ( empty( $ids ) ) {
     3035                        return false;
     3036                }
     3037
     3038                // Check invite IDs and set up strings.
     3039                switch ( $action ) {
     3040                        case 'delete' :
     3041                                // Query for matching invites, and filter out bad IDs.
     3042                                $args = array(
     3043                                        'id'          => $ids,
     3044                                        'invite_sent' => 'all',
     3045                                        'accepted'    => 'all',
     3046                                );
     3047                                $invites    = bp_members_invitations_get_invites( $args );
     3048                                $invite_ids = wp_list_pluck( $invites, 'id' );
     3049
     3050                                $header_text = __( 'Delete Invitations', 'buddypress' );
     3051                                if ( 0 === count( $invite_ids ) ) {
     3052                                        $helper_text = __( 'No invites were found, nothing to delete!', 'buddypress' );
     3053                                } else {
     3054                                        $helper_text = _n( 'You are about to delete the following invitation:', 'You are about to delete the following invitations:', count( $invite_ids ), 'buddypress' );
     3055                                }
     3056                                break;
     3057
     3058                        case 'resend' :
     3059                                /**
     3060                                 * Query for matching invites, and filter out bad IDs
     3061                                 * or those that have already been accepted.
     3062                                 */
     3063                                $args = array(
     3064                                        'id'          => $ids,
     3065                                        'invite_sent' => 'all',
     3066                                        'accepted'    => 'pending',
     3067                                );
     3068                                $invites    = bp_members_invitations_get_invites( $args );
     3069                                $invite_ids = wp_list_pluck( $invites, 'id' );
     3070
     3071                                $header_text = __( 'Resend Invitation Emails', 'buddypress' );
     3072                                if ( 0 === count( $invite_ids ) ) {
     3073                                        $helper_text = __( 'No pending invites were found, nothing to resend!', 'buddypress' );
     3074                                } else {
     3075                                        $helper_text = _n( 'You are about to resend an invitation email to the following address:', 'You are about to resend invitation emails to the following addresses:', count( $invite_ids ), 'buddypress' );
     3076                                }
     3077                                break;
     3078                }
     3079
     3080                // These arguments are added to all URLs.
     3081                $url_args = array( 'page' => 'bp-members-invitations' );
     3082
     3083                // These arguments are only added when performing an action.
     3084                $action_args = array(
     3085                        'action'     => 'do_' . $action,
     3086                        'invite_ids' => implode( ',', $invite_ids )
     3087                );
     3088
     3089                if ( is_network_admin() ) {
     3090                        $form_url = network_admin_url( 'admin.php' );
     3091                } else {
     3092                        $form_url = bp_get_admin_url( 'tools.php' );
     3093                }
     3094
     3095                $cancel_url = add_query_arg( $url_args, $base_url );
     3096                $action_url = wp_nonce_url(
     3097                        add_query_arg(
     3098                                array_merge( $url_args, $action_args ),
     3099                                $base_url
     3100                        ),
     3101                        'invitations_' . $action
     3102                );
     3103
     3104                ?>
     3105
     3106                <div class="wrap">
     3107                        <h1 class="wp-heading-inline"><?php echo esc_html( $header_text ); ?></h1>
     3108                        <hr class="wp-header-end">
     3109
     3110                        <p><?php echo esc_html( $helper_text ); ?></p>
     3111
     3112                        <?php if ( $invites ) : ?>
     3113
     3114                                <ol class="bp-invitations-list">
     3115                                        <?php foreach ( $invites as $invite ) :
     3116                                                if ( $invite->invite_sent ) {
     3117                                                        $last_notified = mysql2date( 'Y/m/d g:i:s a', $invite->date_modified );
     3118                                                } else {
     3119                                                        $last_notified = __( 'Not yet notified', 'buddypress');
     3120                                                }
     3121                                                ?>
     3122
     3123                                                <li>
     3124                                                        <strong><?php echo esc_html( $invite->invitee_email ) ?></strong>
     3125
     3126                                                        <?php if ( 'resend' === $action ) : ?>
     3127
     3128                                                                <p class="description">
     3129                                                                        <?php
     3130                                                                        /* translators: %s: notification date */
     3131                                                                        printf( esc_html__( 'Last notified: %s', 'buddypress'), $last_notified );
     3132                                                                        ?>
     3133                                                                </p>
     3134
     3135                                                        <?php endif; ?>
     3136
     3137                                                </li>
     3138
     3139                                        <?php endforeach; ?>
     3140                                </ol>
     3141
     3142                        <?php endif ; ?>
     3143
     3144                        <?php if ( 'delete' === $action ) : ?>
     3145
     3146                                <p><strong><?php esc_html_e( 'This action cannot be undone.', 'buddypress' ) ?></strong></p>
     3147
     3148                        <?php endif; ?>
     3149
     3150                        <?php if ( $invites ) : ?>
     3151
     3152                                <a class="button-primary" href="<?php echo esc_url( $action_url ); ?>" <?php disabled( ! $invites ); ?>><?php esc_html_e( 'Confirm', 'buddypress' ); ?></a>
     3153
     3154                        <?php endif; ?>
     3155
     3156                        <a class="button" href="<?php echo esc_url( $cancel_url ); ?>"><?php esc_html_e( 'Cancel', 'buddypress' ) ?></a>
     3157                </div>
     3158
     3159                <?php
     3160        }
     3161
    25693162}
    25703163endif; // End class_exists check.
  • 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 4db47da8d..ec10398ae 100644
    class BP_Members_Component extends BP_Component { 
    3939                        buddypress()->plugin_dir,
    4040                        array(
    4141                                'adminbar_myaccount_order' => 20,
    42                                 'search_query_arg' => 'members_search',
     42                                'search_query_arg'         => 'members_search',
     43                                'features'                 => array( 'invitations' )
    4344                        )
    4445                );
    4546        }
    class BP_Members_Component extends BP_Component { 
    6465                        'blocks',
    6566                        'widgets',
    6667                        'cache',
     68                        'invitations',
    6769                );
    6870
    6971                if ( bp_is_active( 'activity' ) ) {
    class BP_Members_Component extends BP_Component { 
    137139                        // Theme compatibility.
    138140                        new BP_Registration_Theme_Compat();
    139141                }
     142
     143                // Invitations.
     144                if ( is_user_logged_in() && bp_is_user_members_invitations() ) {
     145                        if ( bp_is_user_members_invitations_list() ) {
     146                                require $this->path . 'bp-members/screens/list-invites.php';
     147                        } else {
     148                                require $this->path . 'bp-members/screens/send-invites.php';
     149                        }
     150                }
    140151        }
    141152
    142153        /**
    class BP_Members_Component extends BP_Component { 
    233244                        $bp->profile->slug = 'profile';
    234245                        $bp->profile->id   = 'profile';
    235246                }
     247
     248                /** Network Invitations **************************************************
     249                 */
     250
     251                $bp->members->invitations = new stdClass;
    236252        }
    237253
    238254        /**
    class BP_Members_Component extends BP_Component { 
    468484                        }
    469485                }
    470486
    471 
    472487                parent::setup_nav( $main_nav, $sub_nav );
    473488        }
    474489
    class BP_Members_Component extends BP_Component { 
    549564                return $wp_admin_nav;
    550565        }
    551566
     567        /**
     568         * Get the members invitations admin bar navs.
     569         *
     570         * @since 8.0.0
     571         *
     572         * @param  string $admin_bar_menu_id The Admin bar menu ID to attach sub items to.
     573         * @return array                     The members invitations admin navs.
     574         */
     575        public function get_members_invitations_admin_navs( $admin_bar_menu_id = '' ) {
     576                $wp_admin_nav = array();
     577                $invite_link  = trailingslashit( bp_loggedin_user_domain() . bp_get_profile_slug() );
     578
     579                if ( ! $admin_bar_menu_id ) {
     580                        $admin_bar_menu_id = $this->id;
     581                }
     582
     583                return $wp_admin_nav;
     584        }
     585
    552586        /**
    553587         * Set up the Admin Bar.
    554588         *
    class BP_Members_Component extends BP_Component { 
    590624                        } else {
    591625                                add_filter( 'bp_xprofile_admin_nav', array( $this, 'setup_xprofile_admin_nav' ), 2 );
    592626                        }
     627
     628                        // $wp_admin_nav = array_merge( $wp_admin_nav, $this->get_members_invitations_admin_navs() );
    593629                }
    594630
    595631                parent::setup_admin_bar( $wp_admin_nav );
  • new file src/bp-members/classes/class-bp-members-invitation-manager.php

    diff --git src/bp-members/classes/class-bp-members-invitation-manager.php src/bp-members/classes/class-bp-members-invitation-manager.php
    new file mode 100644
    index 000000000..139caf279
    - +  
     1<?php
     2/**
     3 * Membership invitations class.
     4 *
     5 * @package BuddyPress
     6 * @subpackage Core
     7 * @since 8.0.0
     8 */
     9
     10// Exit if accessed directly.
     11defined( 'ABSPATH' ) || exit;
     12
     13/**
     14 * Membership invitations class.
     15 *
     16 * An extension of the core Invitations class that adapts the
     17 * core logic to accommodate site membership invitation behavior.
     18 *
     19 * @since 8.0.0
     20 */
     21class BP_Members_Invitation_Manager extends BP_Invitation_Manager {
     22        /**
     23         * Construct parameters.
     24         *
     25         * @since 8.0.0
     26         *
     27         * @param array|string $args.
     28         */
     29        public function __construct( $args = '' ) {
     30                parent::__construct();
     31        }
     32
     33        /**
     34         * This is where custom actions are added to run when notifications of an
     35         * invitation or request need to be generated & sent.
     36         *
     37         * @since 8.0.0
     38         *
     39         * @param obj BP_Invitation $invitation The invitation to send.
     40         * @return bool True on success, false on failure.
     41         */
     42        public function run_send_action( BP_Invitation $invitation ) {
     43                // Notify site admins of the pending request
     44                if ( 'request' === $invitation->type ) {
     45                        // @TODO
     46                        return true;
     47                // Notify the invitee of the invitation.
     48                } else {
     49                        // Stop if the invitation has already been accepted.
     50                        if ( $invitation->accepted ) {
     51                                return false;
     52                        }
     53
     54                        $inviter_ud = bp_core_get_core_userdata( $invitation->inviter_id );
     55
     56                        $invite_url = esc_url(
     57                                add_query_arg(
     58                                        array(
     59                                                'inv' => $invitation->id,
     60                                                'ih'  => bp_members_invitations_get_hash( $invitation ),
     61                                        ), bp_get_signup_page()
     62                                )
     63                        );
     64                        $unsubscribe_args = array(
     65                                'user_id'           => 0,
     66                                'email_address'     => $invitation->invitee_email,
     67                                'notification_type' => 'bp-members-invitation',
     68                        );
     69
     70                        $args = array(
     71                                'tokens' => array(
     72                                        'inviter.name'        => bp_core_get_userlink( $invitation->inviter_id, true, false, true ),
     73                                        'inviter.url'         => bp_core_get_user_domain( $invitation->inviter_id ),
     74                                        'inviter.id'          => $invitation->inviter_id,
     75                                        'invite.accept_url'   => esc_url( $invite_url ),
     76                                        'usermessage'         => $invitation->content,
     77                                        'unsubscribe'         => esc_url( bp_email_get_unsubscribe_link( $unsubscribe_args ) ),
     78                                ),
     79                        );
     80
     81                        return bp_send_email( 'bp-members-invitation', $invitation->invitee_email, $args );
     82                }
     83        }
     84
     85        /**
     86         * This is where custom actions are added to run when an invitation
     87         * or request is accepted.
     88         *
     89         * @since 8.0.0
     90         *
     91         * @param string $type Are we accepting an invitation or request?
     92         * @param array  $r    Parameters that describe the invitation being accepted.
     93         * @return bool True on success, false on failure.
     94         */
     95        public function run_acceptance_action( $type = 'invite', $r  ) {
     96                // If the user is already a member (because BP at one point allowed two invitations to
     97                // slip through), return early.
     98
     99                if ( 'request' === $type ) {
     100                        /**
     101                         * Fires after a network membership request has been accepted.
     102                         *
     103                         * @since 8.0.0
     104                         *
     105                         * @param int  $user_id    ID of the user who accepted membership.
     106                         * @param int  $network_id ID of the network that was accepted membership to.
     107                         */
     108                        do_action( 'network_membership_request_accepted', $r['user_id'], $r['item_id'] );
     109                } else {
     110                        /**
     111                         * Fires after a user has accepted a site membership invite.
     112                         *
     113                         * @since 8.0.0
     114                         *
     115                         * @param int $user_id    ID of the user who accepted the membership invite.
     116                         * @param int $inviter_id ID of the user who invited this user to the group.
     117                         */
     118                        do_action( 'network_membership_invite_accepted', $r['user_id'], $inviter_id );
     119                }
     120
     121
     122                return true;
     123        }
     124
     125        /**
     126         * Should this invitation be created?
     127         *
     128         * @since 8.0.0
     129         *
     130         * @param array $args.
     131         * @return bool
     132         */
     133        public function allow_invitation( $args ) {
     134                // Does the inviter have this capability?
     135                if ( ! bp_user_can( $args['inviter_id'], 'bp_members_send_invitation' ) ) {
     136                        return false;
     137                }
     138
     139                // Is the invited user eligible to receive an invitation? Hasn't opted out?
     140                if ( ! bp_user_can( 0, 'bp_members_receive_invitation', $args ) ) {
     141                        return false;
     142                }
     143
     144                return true;
     145        }
     146
     147        /**
     148         * Should this request be created?
     149         *
     150         * @since 8.0.0
     151         *
     152         * @param array $args.
     153         * @return bool.
     154         */
     155        public function allow_request( $args ) {
     156                // Does the requester have this capability?
     157                if ( ! bp_user_can( 0, 'bp_network_request_membership', $args ) ) {
     158                        return false;
     159                }
     160
     161                return true;
     162        }
     163}
  • new file src/bp-members/classes/class-bp-members-invitations-list-table.php

    diff --git src/bp-members/classes/class-bp-members-invitations-list-table.php src/bp-members/classes/class-bp-members-invitations-list-table.php
    new file mode 100644
    index 000000000..90f35d921
    - +  
     1<?php
     2/**
     3 * BuddyPress Membership Invitation List Table class.
     4 *
     5 * @package BuddyPress
     6 * @subpackage MembersAdminClasses
     7 * @since 8.0.0
     8 */
     9
     10// Exit if accessed directly.
     11defined( 'ABSPATH' ) || exit;
     12
     13/**
     14 * List table class for signups admin page.
     15 *
     16 * @since 8.0.0
     17 */
     18class BP_Members_Invitations_List_Table extends WP_Users_List_Table {
     19
     20        /**
     21         * The type of view currently being displayed.
     22         *
     23         * E.g. "All", "Pending", "Sent", "Unsent"...
     24         *
     25         * @since 8.0.0
     26         * @var string
     27         */
     28        public $active_filters = array();
     29
     30        /**
     31         * Signup counts.
     32         *
     33         * @since 8.0.0
     34         *
     35         * @var int
     36         */
     37        public $total_items = 0;
     38
     39        /**
     40         * Constructor.
     41         *
     42         * @since 8.0.0
     43         */
     44        public function __construct() {
     45                // Define singular and plural labels, as well as whether we support AJAX.
     46                parent::__construct( array(
     47                        'ajax'     => false,
     48                        'plural'   => 'invitations',
     49                        'singular' => 'invitation',
     50                        'screen'   => get_current_screen()->id,
     51                ) );
     52        }
     53
     54        /**
     55         * Set up items for display in the list table.
     56         *
     57         * Handles filtering of data, sorting, pagination, and any other data
     58         * manipulation required prior to rendering.
     59         *
     60         * @since 8.0.0
     61         */
     62        public function prepare_items() {
     63                global $usersearch;
     64
     65                $search   = isset( $_REQUEST['s'] ) ? $_REQUEST['s'] : '';
     66                $per_page = $this->get_items_per_page( str_replace( '-', '_', "{$this->screen->id}_per_page" ) );
     67                $paged    = $this->get_pagenum();
     68
     69                $args = array(
     70                        'invite_sent'       => 'all',
     71                        'accepted'          => 'all',
     72                        'search_terms'      => $search,
     73                        'order_by'          => 'date_modified',
     74                        'sort_order'        => 'DESC',
     75                        'page'              => $paged,
     76                        'per_page'          => $per_page,
     77                );
     78
     79                if ( isset( $_REQUEST['accepted'] ) && in_array( $_REQUEST['accepted'], array( 'pending', 'accepted' ), true ) ) {
     80                        $args['accepted']       = $_REQUEST['accepted'];
     81                        $this->active_filters[] = $_REQUEST['accepted'];
     82                }
     83                if ( isset( $_REQUEST['sent'] ) && in_array( $_REQUEST['sent'], array( 'draft', 'sent' ), true ) ) {
     84                        $args['invite_sent']    = $_REQUEST['sent'];
     85                        $this->active_filters[] = $_REQUEST['sent'];
     86                }
     87
     88                if ( isset( $_REQUEST['orderby'] ) ) {
     89                        $args['order_by'] = $_REQUEST['orderby'];
     90                }
     91
     92                if ( isset( $_REQUEST['order'] ) ) {
     93                        $args['sort_order'] = $_REQUEST['order'];
     94                }
     95
     96                $invites_class     = new BP_Members_Invitation_Manager();
     97                $this->items       = $invites_class->get_invitations( $args );
     98                $this->total_items = $invites_class->get_invitations_total_count( $args );
     99
     100                $this->set_pagination_args( array(
     101                        'total_items' => $this->total_items,
     102                        'per_page'    => $per_page,
     103                ) );
     104        }
     105
     106        /**
     107         * Get the list of views available on this table (e.g. "all", "public").
     108         *
     109         * @since 8.0.0
     110         */
     111        public function views() {
     112                $url_base = add_query_arg(
     113                        array(
     114                                'page' => 'bp-members-invitations',
     115                        ),
     116                        bp_get_admin_url( 'users.php' )
     117                );
     118
     119                ?>
     120
     121                <h2 class="screen-reader-text"><?php
     122                        /* translators: accessibility text */
     123                        _e( 'Filter invitations list', 'buddypress' );
     124                ?></h2>
     125                <ul class="subsubsub">
     126                        <li class="all">
     127                                <a href="<?php echo esc_url( $url_base ); ?>" class="<?php if ( empty( $this->active_filters ) ) echo 'current'; ?>">
     128                                        <?php esc_html_e( 'All', 'buddypress' ); ?>
     129                                </a> |
     130                        </li>
     131                        <li class="pending">
     132                                <a href="<?php echo esc_url( add_query_arg( 'accepted', 'pending', $url_base ) ); ?>" class="<?php if ( in_array( 'pending', $this->active_filters, true ) ) echo 'current'; ?>">
     133                                        <?php esc_html_e( 'Pending', 'buddypress' ); ?>
     134                                </a> |
     135                        </li>
     136                        <li class="accepted">
     137                                <a href="<?php echo esc_url( add_query_arg( 'accepted', 'accepted', $url_base ) ); ?>" class="<?php if ( in_array( 'accepted', $this->active_filters, true ) ) echo 'current'; ?>">
     138                                        <?php esc_html_e( 'Accepted', 'buddypress' ); ?>
     139                                </a> |
     140                        </li>
     141                        <li class="draft">
     142                                <a href="<?php echo esc_url( add_query_arg( 'sent', 'draft', $url_base ) ); ?>" class="<?php if ( in_array( 'draft', $this->active_filters, true ) ) echo 'current'; ?>">
     143                                        <?php esc_html_e( 'Draft (Unsent)', 'buddypress' ); ?>
     144                                </a> |
     145                        </li>
     146                        <li class="sent">
     147                                <a href="<?php echo esc_url( add_query_arg( 'sent', 'sent', $url_base ) ); ?>" class="<?php if ( in_array( 'sent', $this->active_filters, true ) ) echo 'current'; ?>">
     148                                        <?php esc_html_e( 'Sent', 'buddypress' ); ?>
     149                                </a> |
     150                        </li>
     151
     152                        <?php
     153
     154                        /**
     155                         * Fires inside listing of views so plugins can add their own.
     156                         *
     157                         * @since 8.0.0
     158                         *
     159                         * @param string $url_base       Current URL base for view.
     160                         * @param array  $active_filters Current filters being requested.
     161                         */
     162                        do_action( 'bp_members_invitations_list_table_get_views', $url_base, $this->active_filters ); ?>
     163                </ul>
     164        <?php
     165        }
     166
     167        /**
     168         * Get rid of the extra nav.
     169         *
     170         * WP_Users_List_Table will add an extra nav to change user's role.
     171         * As we're dealing with invitations, we don't need this.
     172         *
     173         * @since 8.0.0
     174         *
     175         * @param array $which Current table nav item.
     176         */
     177        public function extra_tablenav( $which ) {
     178                return;
     179        }
     180
     181        /**
     182         * Specific signups columns.
     183         *
     184         * @since 8.0.0
     185         *
     186         * @return array
     187         */
     188        public function get_columns() {
     189
     190                /**
     191                 * Filters the single site Members signup columns.
     192                 *
     193                 * @since 8.0.0
     194                 *
     195                 * @param array $value Array of columns to display.
     196                 */
     197                return apply_filters( 'bp_members_invitations_list_columns', array(
     198                        'cb'                       => '<input type="checkbox" />',
     199                        'invitee_email'            => __( 'Invitee',    'buddypress' ),
     200                        'username'                 => __( 'Inviter',        'buddypress' ),
     201                        'inviter_registered_date'  => __( 'Inviter Registered',  'buddypress' ),
     202                        'invitation_date_modified' => __( 'Date Modified',   'buddypress' ),
     203                        'invitation_sent'          => __( 'Email Sent', 'buddypress' ),
     204                        'invitation_accepted'      => __( 'Accepted', 'buddypress' )
     205                ) );
     206        }
     207
     208        /**
     209         * Specific bulk actions for signups.
     210         *
     211         * @since 8.0.0
     212         */
     213        public function get_bulk_actions() {
     214                $actions = array(
     215                        'resend' => _x( 'Resend Email', 'Pending invitation action', 'buddypress' ),
     216                );
     217
     218                if ( current_user_can( 'delete_users' ) ) {
     219                        $actions['delete'] = _x( 'Delete', 'Pending invitation action', 'buddypress' );
     220                }
     221
     222                return $actions;
     223        }
     224
     225        /**
     226         * The text shown when no items are found.
     227         *
     228         * Nice job, clean sheet!
     229         *
     230         * @since 8.0.0
     231         */
     232        public function no_items() {
     233
     234                if ( bp_get_members_invitations_allowed() ) {
     235                        esc_html_e( 'No pending invitations found.', 'buddypress' );
     236                } else {
     237                        $link = sprintf( '<a href="%1$s">%2$s</a>', esc_url( bp_get_admin_url( add_query_arg( array( 'page' => 'bp-settings' ), 'admin.php' ) ) ), esc_html__( 'Edit settings', 'buddypress' ) );
     238
     239                        /* translators: %s: url to site settings */
     240                        printf( __( 'Invitations are not allowed. %s', 'buddypress' ), $link );
     241                }
     242
     243        }
     244
     245        /**
     246         * The columns invitations can be reordered by.
     247         *
     248         * @since 8.0.0
     249         */
     250        public function get_sortable_columns() {
     251                return array(
     252                        'invitee_email'            => 'invitee_email',
     253                        'username'                 => 'inviter_id',
     254                        'invitation_date_modified' => 'date_modified',
     255                        'invitation_sent'          => 'invite_sent',
     256                        'invitation_accepted'      => 'accepted',
     257                );
     258        }
     259
     260        /**
     261         * Display invitation rows.
     262         *
     263         * @since 8.0.0
     264         */
     265        public function display_rows() {
     266                $style = '';
     267                foreach ( $this->items as $invite ) {
     268                        $style = ( ' class="alternate"' == $style ) ? '' : ' class="alternate"';
     269                        echo "\n\t" . $this->single_row( $invite, $style );
     270                }
     271        }
     272
     273        /**
     274         * Display an invitation row.
     275         *
     276         * @since 8.0.0
     277         *
     278         * @see WP_List_Table::single_row() for explanation of params.
     279         *
     280         * @param BP_Invitation $invite   BP_Invitation object.
     281         * @param string        $style    Styles for the row.
     282         * @param string        $role     Role to be assigned to user.
     283         * @param int           $numposts Number of posts.
     284         * @return void
     285         */
     286        public function single_row( $invite = null, $style = '', $role = '', $numposts = 0 ) {
     287                echo '<tr' . $style . ' id="invitation-' . esc_attr( $invite->id ) . '">';
     288                echo $this->single_row_columns( $invite );
     289                echo '</tr>';
     290        }
     291
     292        /**
     293         * Markup for the checkbox used to select items for bulk actions.
     294         *
     295         * @since 8.0.0
     296         *
     297         * @param BP_Invitation $invite BP_Invitation object.
     298         */
     299        public function column_cb( $invite = null ) {
     300        ?>
     301                <label class="screen-reader-text" for="invitation_<?php echo intval( $invite->id ); ?>"><?php
     302                        /* translators: accessibility text */
     303                        printf( esc_html__( 'Select invitation: %s', 'buddypress' ), $invite->id );
     304                ?></label>
     305                <input type="checkbox" id="invitation_<?php echo intval( $invite->id ) ?>" name="invite_ids[]" value="<?php echo esc_attr( $invite->id ) ?>" />
     306                <?php
     307        }
     308
     309        /**
     310         * Markup for the checkbox used to select items for bulk actions.
     311         *
     312         * @since 8.0.0
     313         *
     314         * @param BP_Invitation $invite BP_Invitation object.
     315         */
     316        public function column_invitee_email( $invite = null ) {
     317                echo esc_html( $invite->invitee_email );
     318
     319                $actions = array();
     320
     321                // Resend action only if pending
     322                if ( ! $invite->accepted ) {
     323                        // Resend invitation email link.
     324                        $email_link = add_query_arg(
     325                                array(
     326                                        'page'      => 'bp-members-invitations',
     327                                        'invite_id' => $invite->id,
     328                                        'action'    => 'resend',
     329                                ),
     330                                bp_get_admin_url( 'users.php' )
     331                        );
     332                        if ( ! $invite->invite_sent ) {
     333                                $resend_label = __( 'Send', 'buddypress' );
     334                        } else {
     335                                $resend_label = __( 'Resend', 'buddypress' );
     336                        }
     337                        $actions['resend'] = sprintf( '<a href="%1$s">%2$s</a>', esc_url( $email_link ), $resend_label );
     338                }
     339
     340                // Delete link. Could be cleanup or revoking the invitation.
     341                $delete_link = add_query_arg(
     342                        array(
     343                                'page'      => 'bp-members-invitations',
     344                                'invite_id' => $invite->id,
     345                                'action'    => 'delete',
     346                        ),
     347                        bp_get_admin_url( 'users.php' )
     348                );
     349                // Two cases: unsent and accepted (cleanup), and pending (cancels invite).
     350                if ( ! $invite->invite_sent || $invite->accepted ) {
     351                        $actions['delete'] = sprintf( '<a href="%1$s" class="delete">%2$s</a>', esc_url( $delete_link ), __( 'Delete', 'buddypress' ) );
     352                } else {
     353                        $actions['delete'] = sprintf( '<a href="%1$s" class="delete">%2$s</a>', esc_url( $delete_link ), __( 'Cancel', 'buddypress' ) );
     354                }
     355
     356                /**
     357                 * Filters the row actions for each invitation in list.
     358                 *
     359                 * @since 8.0.0
     360                 *
     361                 * @param array  $actions Array of actions and corresponding links.
     362                 * @param object $invite  The BP_Invitation.
     363                 */
     364                $actions = apply_filters( 'bp_members_invitations_management_row_actions', $actions, $invite );
     365
     366                echo $this->row_actions( $actions );
     367        }
     368
     369        /**
     370         * Display invited user's email address.
     371         *
     372         * @since 8.0.0
     373         *
     374         * @param BP_Invitation $invite BP_Invitation object.
     375         */
     376        public function column_email( $invite = null ) {
     377                printf( '<a href="mailto:%1$s">%2$s</a>', esc_attr( $invite->user_email ), esc_html( $invite->user_email ) );
     378        }
     379
     380        /**
     381         * The inviter.
     382         *
     383         * @since 8.0.0
     384         *
     385         * @param BP_Invitation $invite BP_Invitation object.
     386         */
     387        public function column_username( $invite = null ) {
     388                $avatar = get_avatar( $invite->inviter_id, 32 );
     389                $inviter = get_user_by( 'id', $invite->inviter_id );
     390                if ( ! $inviter ) {
     391                        return;
     392                }
     393                $user_link = bp_core_get_user_domain( $invite->inviter_id );
     394                echo $avatar . sprintf( '<strong><a href="%1$s" class="edit">%2$s</a></strong><br/>', esc_url( $user_link ), $inviter->user_login );
     395        }
     396
     397        /**
     398         * Display invitation date.
     399         *
     400         * @since 8.0.0
     401         *
     402         * @param BP_Invitation $invite BP_Invitation object.
     403         */
     404        public function column_inviter_registered_date( $invite = null ) {
     405                $inviter = get_user_by( 'id', $invite->inviter_id );
     406                if ( ! $inviter ) {
     407                        return;
     408                }
     409                echo esc_html( $inviter->user_registered );
     410        }
     411
     412        /**
     413         * Display invitation date.
     414         *
     415         * @since 8.0.0
     416         *
     417         * @param BP_Invitation $invite BP_Invitation object.
     418         */
     419        public function column_invitation_date_modified( $invite = null ) {
     420                echo esc_html( $invite->date_modified );
     421        }
     422
     423        /**
     424         * Display invitation date.
     425         *
     426         * @since 8.0.0
     427         *
     428         * @param BP_Invitation $invite BP_Invitation object.
     429         */
     430        public function column_invitation_sent( $invite = null ) {
     431                if ( $invite->invite_sent) {
     432                        esc_html_e( 'Yes', 'buddypress' );
     433                } else {
     434                        esc_html_e( 'No', 'buddypress' );
     435                }
     436        }
     437
     438        /**
     439         * Display invitation acceptance status.
     440         *
     441         * @since 8.0.0
     442         *
     443         * @param BP_Invitation $invite BP_Invitation object.
     444         */
     445        public function column_invitation_accepted( $invite = null ) {
     446                if ( $invite->accepted ) {
     447                        esc_html_e( 'Yes', 'buddypress' );
     448                } else {
     449                        esc_html_e( 'No', 'buddypress' );
     450                }
     451        }
     452
     453        /**
     454         * Allow plugins to add their custom column.
     455         *
     456         * @since 8.0.0
     457         *
     458         * @param BP_Invitation $invite      BP_Invitation object.
     459         * @param string        $column_name The column name.
     460         * @return string
     461         */
     462        function column_default( $invite = null, $column_name = '' ) {
     463
     464                /**
     465                 * Filters the single site custom columns for plugins.
     466                 *
     467                 * @since 8.0.0
     468                 *
     469                 * @param string $column_name The column name.
     470                 * @param object $invite      The BP_Invitation object..
     471                 */
     472                return apply_filters( 'bp_members_invitations_management_custom_column', '', $column_name, $invite );
     473        }
     474}
  • new file src/bp-members/classes/class-bp-members-invitations-template.php

    diff --git src/bp-members/classes/class-bp-members-invitations-template.php src/bp-members/classes/class-bp-members-invitations-template.php
    new file mode 100644
    index 000000000..1c5da5c42
    - +  
     1<?php
     2/**
     3 * BuddyPress Members Invitation Template Loop Class.
     4 *
     5 * @package BuddyPress
     6 * @subpackage TonificationsTemplate
     7 * @since 8.0.0
     8 */
     9
     10// Exit if accessed directly.
     11defined( 'ABSPATH' ) || exit;
     12
     13/**
     14 * The main membership invitations template loop class.
     15 *
     16 * Responsible for loading a group of membership invitations into a loop for display.
     17 *
     18 * @since 8.0.0
     19 */
     20class BP_Members_Invitations_Template {
     21
     22        /**
     23         * The loop iterator.
     24         *
     25         * @since 8.0.0
     26         * @var int
     27         */
     28        public $current_invitation = -1;
     29
     30        /**
     31         * The number of invitations returned by the paged query.
     32         *
     33         * @since 8.0.0
     34         * @var int
     35         */
     36        public $current_invitation_count;
     37
     38        /**
     39         * Total number of invitations matching the query.
     40         *
     41         * @since 8.0.0
     42         * @var int
     43         */
     44        public $total_invitation_count;
     45
     46        /**
     47         * Array of network invitations located by the query.
     48         *
     49         * @since 8.0.0
     50         * @var array
     51         */
     52        public $invitations;
     53
     54        /**
     55         * The invitation object currently being iterated on.
     56         *
     57         * @since 8.0.0
     58         * @var object
     59         */
     60        public $invitation;
     61
     62        /**
     63         * A flag for whether the loop is currently being iterated.
     64         *
     65         * @since 8.0.0
     66         * @var bool
     67         */
     68        public $in_the_loop;
     69
     70        /**
     71         * The ID of the user to whom the displayed invitations were sent.
     72         *
     73         * @since 8.0.0
     74         * @var int
     75         */
     76        public $user_id;
     77
     78        /**
     79         * The ID of the user to whom the displayed invitations belong.
     80         *
     81         * @since 8.0.0
     82         * @var int
     83         */
     84        public $inviter_id;
     85
     86        /**
     87         * The page number being requested.
     88         *
     89         * @since 8.0.0
     90         * @var int
     91         */
     92        public $pag_page;
     93
     94        /**
     95         * The $_GET argument used in URLs for determining pagination.
     96         *
     97         * @since 8.0.0
     98         * @var int
     99         */
     100        public $pag_arg;
     101
     102        /**
     103         * The number of items to display per page of results.
     104         *
     105         * @since 8.0.0
     106         * @var int
     107         */
     108        public $pag_num;
     109
     110        /**
     111         * An HTML string containing pagination links.
     112         *
     113         * @since 8.0.0
     114         * @var string
     115         */
     116        public $pag_links;
     117
     118        /**
     119         * A string to match against.
     120         *
     121         * @since 8.0.0
     122         * @var string
     123         */
     124        public $search_terms;
     125
     126        /**
     127         * A database column to order the results by.
     128         *
     129         * @since 8.0.0
     130         * @var string
     131         */
     132        public $order_by;
     133
     134        /**
     135         * The direction to sort the results (ASC or DESC).
     136         *
     137         * @since 8.0.0
     138         * @var string
     139         */
     140        public $sort_order;
     141
     142        /**
     143         * Array of variables used in this invitation query.
     144         *
     145         * @since 2.2.2
     146         * @var array
     147         */
     148        public $query_vars;
     149
     150        /**
     151         * Constructor method.
     152         *
     153         * @see bp_has_members_invitations() For information on the array format.
     154         *
     155         * @since 8.0.0
     156         *
     157         * @param array $args {
     158         *     An array of arguments. See {@link bp_has_members_invitations()}
     159         *     for more details.
     160         * }
     161         */
     162        public function __construct( $args = array() ) {
     163
     164                // Parse arguments.
     165                $r = wp_parse_args( $args, array(
     166                        'id'                => false,
     167                        'user_id'           => false,
     168                        'inviter_id'        => false,
     169                        'invitee_email'     => false,
     170                        'item_id'           => false,
     171                        'type'              => 'invite',
     172                        'invite_sent'       => 'all',
     173                        'accepted'          => 'all',
     174                        'search_terms'      => '',
     175                        'order_by'          => 'date_modified',
     176                        'sort_order'        => 'DESC',
     177                        'page'              => 1,
     178                        'per_page'          => 25,
     179                        'fields'            => 'all',
     180                        'page_arg'          => 'ipage',
     181                ) );
     182
     183                // Sort order direction.
     184                $orders = array( 'ASC', 'DESC' );
     185                if ( ! empty( $_GET['sort_order'] ) && in_array( $_GET['sort_order'], $orders ) ) {
     186                        $r['sort_order'] = $_GET['sort_order'];
     187                } else {
     188                        $r['sort_order'] = in_array( $r['sort_order'], $orders ) ? $r['sort_order'] : 'DESC';
     189                }
     190
     191                // Setup variables.
     192                $this->pag_arg      = sanitize_key( $r['page_arg'] );
     193                $this->pag_page     = bp_sanitize_pagination_arg( $this->pag_arg, $r['page']     );
     194                $this->pag_num      = bp_sanitize_pagination_arg( 'num',          $r['per_page'] );
     195                $this->user_id      = $r['user_id'];
     196                $this->search_terms = $r['search_terms'];
     197                $this->order_by     = $r['order_by'];
     198                $this->sort_order   = $r['sort_order'];
     199                $this->query_vars   = array(
     200                        'id'                => $r['id'],
     201                        'user_id'           => $r['user_id'],
     202                        'inviter_id'        => $r['inviter_id'],
     203                        'invitee_email'     => $r['invitee_email'],
     204                        'item_id'           => $r['item_id'],
     205                        'type'              => $r['type'],
     206                        'invite_sent'       => $r['invite_sent'],
     207                        'accepted'          => $r['accepted'],
     208                        'search_terms'      => $this->search_terms,
     209                        'order_by'          => $this->order_by,
     210                        'sort_order'        => $this->sort_order,
     211                        'page'              => $this->pag_page,
     212                        'per_page'          => $this->pag_num,
     213                );
     214
     215                // Setup the invitations to loop through.
     216                $invites_class = new BP_Members_Invitation_Manager();
     217
     218                $this->invitations              = $invites_class->get_invitations( $this->query_vars );
     219                $this->current_invitation_count = count( $this->invitations );
     220                $this->total_invitation_count   = $invites_class->get_invitations_total_count( $this->query_vars );
     221
     222                if ( (int) $this->total_invitation_count && (int) $this->pag_num ) {
     223                        $add_args = array(
     224                                'sort_order' => $this->sort_order,
     225                        );
     226
     227                        $this->pag_links = paginate_links( array(
     228                                'base'      => add_query_arg( $this->pag_arg, '%#%' ),
     229                                'format'    => '',
     230                                'total'     => ceil( (int) $this->total_invitation_count / (int) $this->pag_num ),
     231                                'current'   => $this->pag_page,
     232                                'prev_text' => _x( '&larr;', 'Network invitation pagination previous text', 'buddypress' ),
     233                                'next_text' => _x( '&rarr;', 'Network invitation pagination next text',     'buddypress' ),
     234                                'mid_size'  => 1,
     235                                'add_args'  => $add_args,
     236                        ) );
     237                }
     238        }
     239
     240        /**
     241         * Whether there are invitations available in the loop.
     242         *
     243         * @since 8.0.0
     244         *
     245         * @see bp_has_members_invitations()
     246         *
     247         * @return bool True if there are items in the loop, otherwise false.
     248         */
     249        public function has_invitations() {
     250                if ( $this->current_invitation_count ) {
     251                        return true;
     252                }
     253
     254                return false;
     255        }
     256
     257        /**
     258         * Set up the next invitation and iterate index.
     259         *
     260         * @since 8.0.0
     261         *
     262         * @return object The next invitation to iterate over.
     263         */
     264        public function next_invitation() {
     265
     266                $this->current_invitation++;
     267
     268                $this->invitation = $this->invitations[ $this->current_invitation ];
     269
     270                return $this->invitation;
     271        }
     272
     273        /**
     274         * Rewind the blogs and reset blog index.
     275         *
     276         * @since 8.0.0
     277         */
     278        public function rewind_invitations() {
     279
     280                $this->current_invitation = -1;
     281
     282                if ( $this->current_invitation_count > 0 ) {
     283                        $this->invitation = $this->invitations[0];
     284                }
     285        }
     286
     287        /**
     288         * Whether there are invitations left in the loop to iterate over.
     289         *
     290         * This method is used by {@link bp_members_invitations()} as part of the
     291         * while loop that controls iteration inside the invitations loop, eg:
     292         *     while ( bp_members_invitations() ) { ...
     293         *
     294         * @since 8.0.0
     295         *
     296         * @see bp_members_invitations()
     297         *
     298         * @return bool True if there are more invitations to show,
     299         *              otherwise false.
     300         */
     301        public function invitations() {
     302
     303                if ( $this->current_invitation + 1 < $this->current_invitation_count ) {
     304                        return true;
     305
     306                } elseif ( $this->current_invitation + 1 === $this->current_invitation_count ) {
     307
     308                        /**
     309                         * Fires right before the rewinding of invitation posts.
     310                         *
     311                         * @since 8.0.0
     312                         */
     313                        do_action( 'members_invitations_loop_end');
     314
     315                        $this->rewind_invitations();
     316                }
     317
     318                $this->in_the_loop = false;
     319                return false;
     320        }
     321
     322        /**
     323         * Set up the current invitation inside the loop.
     324         *
     325         * Used by {@link bp_the_invitation()} to set up the current
     326         * invitation data while looping, so that template tags used during
     327         * that iteration make reference to the current invitation.
     328         *
     329         * @since 8.0.0
     330         *
     331         * @see bp_the_invitation()
     332         */
     333        public function the_invitation() {
     334                $this->in_the_loop  = true;
     335                $this->invitation = $this->next_invitation();
     336
     337                // Loop has just started.
     338                if ( 0 === $this->current_invitation ) {
     339
     340                        /**
     341                         * Fires if the current invitation item is the first in the invitation loop.
     342                         *
     343                         * @since 8.0.0
     344                         */
     345                        do_action( 'members_invitations_loop_start' );
     346                }
     347        }
     348}
  • new file src/bp-members/screens/list-invites.php

    diff --git src/bp-members/screens/list-invites.php src/bp-members/screens/list-invites.php
    new file mode 100644
    index 000000000..8242dd279
    - +  
     1<?php
     2/**
     3 * Members: Sent Invitations Status
     4 *
     5 * @package BuddyPress
     6 * @subpackage MembersScreens
     7 * @since 8.0.0
     8 */
     9
     10/**
     11 * Catch and process the Send Invites page.
     12 *
     13 * @since 8.0.0
     14 */
     15function members_screen_list_sent_invites() {
     16
     17        /**
     18         * Fires before the loading of template for the send membership invitations page.
     19         *
     20         * @since 8.0.0
     21         */
     22        do_action( 'members_screen_list_sent_invites' );
     23
     24        /**
     25         * Filters the template used to display the send membership invitations page.
     26         *
     27         * @since 8.0.0
     28         *
     29         * @param string $template Path to the send membership invitations template to load.
     30         */
     31        bp_core_load_template( apply_filters( 'members_template_list_sent_invites', 'members/single/invitations' ) );
     32}
     33
     34/**
     35 * Handle canceling or resending single invitations.
     36 *
     37 * @since 8.0.0
     38 *
     39 * @return bool
     40 */
     41function bp_members_invitations_action_handling() {
     42
     43        // Bail if not the read screen.
     44        if ( ! bp_is_user_members_invitations_list() ) {
     45                return false;
     46        }
     47
     48        // Get the action.
     49        $action = ! empty( $_GET['action']          ) ? $_GET['action']          : '';
     50        $nonce  = ! empty( $_GET['_wpnonce']        ) ? $_GET['_wpnonce']        : '';
     51        $id     = ! empty( $_GET['invitation_id']   ) ? $_GET['invitation_id']   : '';
     52
     53        // Bail if no action or no ID.
     54        if ( empty( $action ) || empty( $id ) ) {
     55                return false;
     56        }
     57
     58        if ( 'cancel' === $action ) {
     59                // Check the nonce and delete the invitation.
     60                if ( bp_verify_nonce_request( 'bp_members_invitations_cancel_' . $id ) && bp_members_invitations_delete_by_id( $id ) ) {
     61                        bp_core_add_message( __( 'Invitation successfully canceled.', 'buddypress' )          );
     62                } else {
     63                        bp_core_add_message( __( 'There was a problem canceling that invitation.', 'buddypress' ), 'error' );
     64                }
     65        } else if ( 'resend' === $action ) {
     66                // Check the nonce and resend the invitation.
     67                if ( bp_verify_nonce_request( 'bp_network_invitation_resend_' . $id ) && bp_members_invitation_resend_by_id( $id ) ) {
     68                        bp_core_add_message( __( 'Invitation successfully resent.', 'buddypress' )          );
     69                } else {
     70                        bp_core_add_message( __( 'There was a problem resending that invitation.', 'buddypress' ), 'error' );
     71                }
     72        } else {
     73                return false;
     74        }
     75
     76        // Redirect.
     77        $user_id = bp_displayed_user_id();
     78        bp_core_redirect( bp_get_members_invitations_list_invites_permalink( $user_id ) );
     79}
     80add_action( 'bp_actions', 'bp_members_invitations_action_handling' );
  • src/bp-members/screens/register.php

    diff --git src/bp-members/screens/register.php src/bp-members/screens/register.php
    index 54b6facd5..b08f6ece8 100644
    function bp_core_screen_signup() { 
    4242
    4343        $bp->signup->step = 'request-details';
    4444
    45         if ( !bp_get_signup_allowed() ) {
    46                 $bp->signup->step = 'registration-disabled';
     45        // Could the user be accepting an invitation?
     46        $active_invite = false;
     47        if ( bp_get_members_invitations_allowed() ) {
     48                // Check to see if there's a valid invitation.
     49                $maybe_invite = bp_get_members_invitation_from_request();
     50                if ( $maybe_invite->id ) {
     51                        $active_invite = true;
     52                }
     53        }
    4754
     55        if ( ! bp_get_signup_allowed() && ! $active_invite ) {
     56                $bp->signup->step = 'registration-disabled';
    4857                // If the signup page is submitted, validate and save.
    4958        } elseif ( isset( $_POST['signup_submit'] ) && bp_verify_nonce_request( 'bp_new_signup' ) ) {
    5059
  • new file src/bp-members/screens/send-invites.php

    diff --git src/bp-members/screens/send-invites.php src/bp-members/screens/send-invites.php
    new file mode 100644
    index 000000000..6471ebf1d
    - +  
     1<?php
     2/**
     3 * Members: Send Invitations
     4 *
     5 * @package BuddyPress
     6 * @subpackage MembersScreens
     7 * @since 8.0.0
     8 */
     9
     10/**
     11 * Catch and process the Send Invites page.
     12 *
     13 * @since 8.0.0
     14 */
     15function members_screen_send_invites() {
     16
     17        /**
     18         * Fires before the loading of template for the send membership invitations page.
     19         *
     20         * @since 8.0.0
     21         */
     22        do_action( 'members_screen_send_invites' );
     23
     24        /**
     25         * Filters the template used to display the send membership invitations page.
     26         *
     27         * @since 8.0.0
     28         *
     29         * @param string $template Path to the send membership invitations template to load.
     30         */
     31        bp_core_load_template( apply_filters( 'members_template_send_invites', 'members/single/invitations' ) );
     32}
     33
     34/**
     35 * Handle sending invitations.
     36 *
     37 * @since 8.0.0
     38 *
     39 * @return bool
     40 */
     41function bp_network_invitations_catch_send_action() {
     42
     43        // Bail if not the read screen.
     44        if ( ! bp_is_user_members_invitations_send_screen() ) {
     45                return false;
     46        }
     47
     48        // Get the action.
     49        $action  = ! empty( $_REQUEST['action']          ) ? $_REQUEST['action']          : '';
     50        $nonce   = ! empty( $_REQUEST['_wpnonce']        ) ? $_REQUEST['_wpnonce']        : '';
     51        $email   = ! empty( $_REQUEST['invitee_email']   ) ? $_REQUEST['invitee_email']   : '';
     52        $message = ! empty( $_REQUEST['invite_message']  ) ? $_REQUEST['invite_message']  : '';
     53
     54        // Bail if missing required info.
     55        if ( ( 'send-invite' !== $action ) ) {
     56                return false;
     57        }
     58
     59        $invite_args = array(
     60                'invitee_email' => $email,
     61                'inviter_id'    => bp_displayed_user_id(),
     62                'content'       => $message,
     63                'send_invite'   => 1
     64        );
     65
     66        // Check the nonce and delete the invitation.
     67        if ( bp_verify_nonce_request( 'bp_members_invitation_send_' . bp_displayed_user_id() ) && bp_members_invitations_invite_user( $invite_args ) ) {
     68                bp_core_add_message( __( 'Invitation successfully sent!', 'buddypress' )          );
     69        } else {
     70                bp_core_add_message( __( 'There was a problem sending that invitation.', 'buddypress' ), 'error' );
     71        }
     72
     73        // Redirect.
     74        $user_id = bp_displayed_user_id();
     75        bp_core_redirect( bp_get_members_invitations_send_invites_permalink( $user_id ) );
     76}
     77add_action( 'bp_actions', 'bp_network_invitations_catch_send_action' );
  • src/bp-templates/bp-legacy/buddypress-functions.php

    diff --git src/bp-templates/bp-legacy/buddypress-functions.php src/bp-templates/bp-legacy/buddypress-functions.php
    index f4a3e1016..03c09ac00 100644
    function bp_legacy_theme_group_manage_members_add_search() { 
    20142014                <?php
    20152015        endif;
    20162016}
     2017
     2018/**
     2019 * Modify welcome message in Legacy template pack when
     2020 * community invitations are enabled.
     2021 *
     2022 * @since 8.0.0
     2023 *
     2024 * @return string $message The message text.
     2025 */
     2026function bp_members_invitations_add_legacy_welcome_message() {
     2027        $message = bp_members_invitations_get_registration_welcome_message();
     2028        if ( $message ) {
     2029                echo '<p>' . esc_html( $message ) . '</p>';
     2030        }
     2031}
     2032add_action( 'bp_before_register_page', 'bp_members_invitations_add_legacy_welcome_message' );
     2033
     2034
     2035/**
     2036 * Modify "registration disabled" message in Legacy template pack when
     2037 * community invitations are enabled.
     2038 *
     2039 * @since 8.0.0
     2040 *
     2041 * @return string $message The message text.
     2042 */
     2043function bp_members_invitations_add_legacy_registration_disabled_message() {
     2044        $message = bp_members_invitations_get_modified_registration_disabled_message();
     2045        if ( $message ) {
     2046                echo '<p>' . esc_html( $message ) . '</p>';
     2047        }
     2048}
     2049add_action( 'bp_after_registration_disabled', 'bp_members_invitations_add_legacy_registration_disabled_message' );
  • src/bp-templates/bp-legacy/buddypress/members/single/home.php

    diff --git src/bp-templates/bp-legacy/buddypress/members/single/home.php src/bp-templates/bp-legacy/buddypress/members/single/home.php
    index f56af6e9c..bed9da1bf 100644
     
    8989                elseif ( bp_is_user_notifications() ) :
    9090                        bp_get_template_part( 'members/single/notifications' );
    9191
     92                elseif ( bp_is_user_members_invitations() ) :
     93                        bp_get_template_part( 'members/single/invitations' );
     94
    9295                elseif ( bp_is_user_settings() ) :
    9396                        bp_get_template_part( 'members/single/settings' );
    9497
  • new file src/bp-templates/bp-legacy/buddypress/members/single/invitations.php

    diff --git src/bp-templates/bp-legacy/buddypress/members/single/invitations.php src/bp-templates/bp-legacy/buddypress/members/single/invitations.php
    new file mode 100644
    index 000000000..876a2e314
    - +  
     1<?php
     2/**
     3 * BuddyPress - membership invitations
     4 *
     5 * @package BuddyPress
     6 * @subpackage bp-legacy
     7 * @version 8.0.0
     8 */
     9
     10?>
     11
     12<div class="item-list-tabs no-ajax" id="subnav" aria-label="<?php esc_attr_e( 'Member secondary navigation', 'buddypress' ); ?>" role="navigation">
     13        <ul>
     14                <?php bp_get_options_nav(); ?>
     15        </ul>
     16</div>
     17
     18<?php
     19switch ( bp_current_action() ) :
     20
     21        case 'send-invites' :
     22                bp_get_template_part( 'members/single/invitations/send-invites' );
     23                break;
     24
     25        case 'list-invites' :
     26        default :
     27                bp_get_template_part( 'members/single/invitations/list-invites' );
     28                break;
     29
     30endswitch;
     31
  • new file src/bp-templates/bp-legacy/buddypress/members/single/invitations/invitations-loop.php

    diff --git src/bp-templates/bp-legacy/buddypress/members/single/invitations/invitations-loop.php src/bp-templates/bp-legacy/buddypress/members/single/invitations/invitations-loop.php
    new file mode 100644
    index 000000000..f73857cc8
    - +  
     1<?php
     2/**
     3 * BuddyPress - Membership Invitations Loop
     4 *
     5 * @package BuddyPress
     6 * @subpackage bp-legacy
     7 * @version 8.0.0
     8 */
     9
     10?>
     11<form action="" method="post" id="invitations-bulk-management">
     12        <table class="invitations">
     13                <thead>
     14                        <tr>
     15                                <th class="icon"></th>
     16                                <th class="bulk-select-all"><input id="select-all-invitations" type="checkbox">
     17                                        <label class="bp-screen-reader-text" for="select-all-invitations">
     18                                                <?php
     19                                                /* translators: accessibility text */
     20                                                esc_html_e( 'Select all', 'buddypress' );
     21                                                ?>
     22                                        </label>
     23                                </th>
     24                                <th class="title"><?php esc_html_e( 'Invitee', 'buddypress' ); ?></th>
     25                                <th class="content"><?php esc_html_e( 'Message', 'buddypress' ); ?></th>
     26                                <th class="sent"><?php esc_html_e( 'Sent', 'buddypress' ); ?></th>
     27                                <th class="accepted"><?php esc_html_e( 'Accepted', 'buddypress' ); ?></th>
     28                                <th class="date"><?php esc_html_e( 'Date Modified', 'buddypress' ); ?></th>
     29                                <th class="actions"><?php esc_html_e( 'Actions', 'buddypress' ); ?></th>
     30                        </tr>
     31                </thead>
     32
     33                <tbody>
     34
     35                        <?php while ( bp_the_members_invitations() ) : bp_the_members_invitation(); ?>
     36
     37                                <tr>
     38                                        <td></td>
     39                                        <td class="bulk-select-check">
     40                                                <label for="<?php bp_the_members_invitation_property( 'id', 'attribute' ); ?>">
     41                                                        <input id="<?php bp_the_members_invitation_property( 'id', 'attribute' ); ?>" type="checkbox" name="network_invitations[]" value="<?php bp_the_members_invitation_property( 'id', 'attribute' ); ?>" class="invitation-check">
     42                                                        <span class="bp-screen-reader-text">
     43                                                                <?php
     44                                                                        /* translators: accessibility text */
     45                                                                        esc_html_e( 'Select this invitation', 'buddypress' );
     46                                                                ?>
     47                                                        </span>
     48                                                </label>
     49                                        </td>
     50                                        <td class="invitation-invitee"><?php bp_the_members_invitation_property( 'invitee_email' );  ?></td>
     51                                        <td class="invitation-content"><?php bp_the_members_invitation_property( 'content' );  ?></td>
     52                                        <td class="invitation-sent"><?php bp_the_members_invitation_property( 'invite_sent' );  ?></td>
     53                                        <td class="invitation-accepted"><?php bp_the_members_invitation_property( 'accepted' );  ?></td>
     54                                        <td class="invitation-date-modified"><?php bp_the_members_invitation_property( 'date_modified' );   ?></td>
     55                                        <td class="invitation-actions"><?php bp_the_members_invitation_action_links(); ?></td>
     56                                </tr>
     57
     58                        <?php endwhile; ?>
     59
     60                </tbody>
     61        </table>
     62
     63        <div class="invitations-options-nav">
     64                <?php // @TODO //bp_invitations_bulk_management_dropdown(); ?>
     65        </div><!-- .invitations-options-nav -->
     66
     67        <?php wp_nonce_field( 'invitations_bulk_nonce', 'invitations_bulk_nonce' ); ?>
     68</form>
  • new file src/bp-templates/bp-legacy/buddypress/members/single/invitations/list-invites.php

    diff --git src/bp-templates/bp-legacy/buddypress/members/single/invitations/list-invites.php src/bp-templates/bp-legacy/buddypress/members/single/invitations/list-invites.php
    new file mode 100644
    index 000000000..8e299f1d6
    - +  
     1<?php
     2/**
     3 * BuddyPress - Sent Membership Invitations
     4 *
     5 * @package BuddyPress
     6 * @subpackage bp-legacy
     7 * @version 8.0.0
     8 */
     9?>
     10
     11<?php if ( bp_has_members_invitations() ) : ?>
     12
     13        <h2 class="bp-screen-reader-text">
     14                <?php
     15                /* translators: accessibility text */
     16                esc_html_e( 'Invitations', 'buddypress' );
     17                ?>
     18        </h2>
     19
     20        <div id="pag-top" class="pagination no-ajax">
     21                <div class="pag-count" id="invitations-count-top">
     22                        <?php bp_members_invitations_pagination_count(); ?>
     23                </div>
     24
     25                <div class="pagination-links" id="invitations-pag-top">
     26                        <?php bp_members_invitations_pagination_links(); ?>
     27                </div>
     28        </div>
     29
     30        <?php bp_get_template_part( 'members/single/invitations/invitations-loop' ); ?>
     31
     32        <div id="pag-bottom" class="pagination no-ajax">
     33                <div class="pag-count" id="invitations-count-bottom">
     34                        <?php bp_members_invitations_pagination_count(); ?>
     35                </div>
     36
     37                <div class="pagination-links" id="invitations-pag-bottom">
     38                        <?php bp_members_invitations_pagination_links(); ?>
     39                </div>
     40        </div>
     41
     42<?php else : ?>
     43
     44        <p><?php esc_html_e( 'There are no invitations to display.', 'buddypress' ); ?></p>
     45
     46<?php endif;
  • new file src/bp-templates/bp-legacy/buddypress/members/single/invitations/send-invites.php

    diff --git src/bp-templates/bp-legacy/buddypress/members/single/invitations/send-invites.php src/bp-templates/bp-legacy/buddypress/members/single/invitations/send-invites.php
    new file mode 100644
    index 000000000..6eeb3b62f
    - +  
     1<?php
     2/**
     3 * BuddyPress - Sent Membership Invitations
     4 *
     5 * @package BuddyPress
     6 * @subpackage bp-legacy
     7 * @version 8.0.0
     8 */
     9?>
     10<h2 class="bp-screen-reader-text">
     11        <?php
     12        /* translators: accessibility text */
     13        esc_html_e( 'Send Invitations', 'buddypress' );
     14        ?>
     15</h2>
     16
     17<form class="standard-form members-invitation-form" id="members-invitation-form" method="post">
     18        <p class="description"><?php esc_html_e( 'Fill out the form below to invite a new user to join this site. Upon submission of the form, an email will be sent to the invitee containing a link to accept your invitation. You may also add a custom message to the email.', 'buddypress' ); ?></p>
     19
     20        <label for="bp_members_invitation_invitee_email"><?php esc_html_e( 'Email address of new user', 'buddypress' ); ?></label>
     21        <input id="bp_members_invitation_invitee_email" type="email" name="invitee_email" required="required">
     22
     23        <label for="bp_members_invitation_message"><?php esc_html_e( 'Add a personalized message to the invitation (optional)', 'buddypress' ); ?></label>
     24        <textarea id="bp_members_invitation_message" name="invite_message"></textarea>
     25
     26        <input type="hidden" name="action" value="send-invite">
     27
     28        <?php wp_nonce_field( 'bp_members_invitation_send_' . bp_displayed_user_id() ) ?>
     29        <p>
     30                <input id="submit" type="submit" name="submit" class="submit" value="<?php esc_attr_e( 'Send Invitation', 'buddypress' ) ?>" />
     31        </p>
     32</form>
  • src/bp-templates/bp-nouveau/buddypress-functions.php

    diff --git src/bp-templates/bp-nouveau/buddypress-functions.php src/bp-templates/bp-nouveau/buddypress-functions.php
    index 892bf2e33..f93134f74 100644
    class BP_Nouveau extends BP_Theme_Compat { 
    195195                // Set the BP Uri for the Ajax customizer preview.
    196196                add_filter( 'bp_uri', array( $this, 'customizer_set_uri' ), 10, 1 );
    197197
     198                // Modify "registration disabled" and welcome message if invitations are enabled.
     199                add_action( 'bp_nouveau_feedback_messages', array( $this, 'filter_registration_messages' ), 99 );
     200
    198201                /** Override **********************************************************/
    199202
    200203                /**
    class BP_Nouveau extends BP_Theme_Compat { 
    676679
    677680                return $path;
    678681        }
     682        /**
     683         * Modify "registration disabled" message in Nouveau template pack.
     684         * Modify welcome message in Nouveau template pack.
     685         *
     686         * @since 8.0.0
     687         *
     688         * @param array $messages The list of feedback messages.
     689         *
     690         * @return array $messages
     691         */
     692        function filter_registration_messages( $messages ) {
     693                // Change the "registration is disabled" message.
     694                $disallowed_message = bp_members_invitations_get_modified_registration_disabled_message();
     695                if ( $disallowed_message ) {
     696                        $messages['registration-disabled']['message'] = $disallowed_message;
     697                }
     698                // Add information about invitations to the welcome block.
     699                $welcome_message = bp_members_invitations_get_registration_welcome_message();
     700                if ( $welcome_message ) {
     701                        $messages['request-details']['message'] = $welcome_message . $messages['request-details']['message'];
     702                }
     703                return $messages;
     704        }
    679705}
    680706
    681707/**
  • new file src/bp-templates/bp-nouveau/buddypress/members/single/invitations.php

    diff --git src/bp-templates/bp-nouveau/buddypress/members/single/invitations.php src/bp-templates/bp-nouveau/buddypress/members/single/invitations.php
    new file mode 100644
    index 000000000..1fd36bab1
    - +  
     1<?php
     2/**
     3 * BuddyPress - Membership invitations
     4 *
     5 * @since 8.0.0
     6 * @version 8.0.0
     7 */
     8?>
     9
     10<nav class="<?php bp_nouveau_single_item_subnav_classes(); ?>" id="subnav" role="navigation" aria-label="<?php esc_attr_e( 'Groups menu', 'buddypress' ); ?>">
     11        <ul class="subnav">
     12                <?php bp_get_template_part( 'members/single/parts/item-subnav' ); ?>
     13        </ul>
     14</nav><!-- .bp-navs -->
     15
     16<?php
     17switch ( bp_current_action() ) :
     18
     19        case 'send-invites' :
     20                bp_get_template_part( 'members/single/invitations/send-invites' );
     21                break;
     22
     23        case 'list-invites' :
     24        default :
     25                bp_get_template_part( 'members/single/invitations/list-invites' );
     26                break;
     27
     28endswitch;
     29
  • new file src/bp-templates/bp-nouveau/buddypress/members/single/invitations/invitations-loop.php

    diff --git src/bp-templates/bp-nouveau/buddypress/members/single/invitations/invitations-loop.php src/bp-templates/bp-nouveau/buddypress/members/single/invitations/invitations-loop.php
    new file mode 100644
    index 000000000..48576e8cf
    - +  
     1<?php
     2/**
     3 * BuddyPress - Membership Invitations Loop
     4 *
     5 * @since 8.0.0
     6 * @version 8.0.0
     7 */
     8?>
     9<form action="" method="post" id="invitations-bulk-management">
     10        <table class="invitations">
     11                <thead>
     12                        <tr>
     13                                <th class="icon"></th>
     14                                <th class="bulk-select-all"><input id="select-all-invitations" type="checkbox"><label class="bp-screen-reader-text" for="select-all-invitations"><?php
     15                                        /* translators: accessibility text */
     16                                        esc_html_e( 'Select all', 'buddypress' );
     17                                ?></label></th>
     18                                <th class="title"><?php esc_html_e( 'Invitee', 'buddypress' ); ?></th>
     19                                <th class="content"><?php esc_html_e( 'Message', 'buddypress' ); ?></th>
     20                                <th class="sent"><?php esc_html_e( 'Sent', 'buddypress' ); ?></th>
     21                                <th class="accepted"><?php esc_html_e( 'Accepted', 'buddypress' ); ?></th>
     22                                <th class="date"><?php esc_html_e( 'Date Modified', 'buddypress' ); ?></th>
     23                                <th class="actions"><?php esc_html_e( 'Actions', 'buddypress' ); ?></th>
     24                        </tr>
     25                </thead>
     26
     27                <tbody>
     28
     29                        <?php while ( bp_the_members_invitations() ) : bp_the_members_invitation(); ?>
     30
     31                                <tr>
     32                                        <td></td>
     33                                        <td class="bulk-select-check">
     34                                                <label for="<?php bp_the_members_invitation_property( 'id', 'attribute' ); ?>">
     35                                                        <input id="<?php bp_the_members_invitation_property( 'id', 'attribute' ); ?>" type="checkbox" name="network_invitations[]" value="<?php bp_the_members_invitation_property( 'id', 'attribute' ); ?>" class="invitation-check">
     36                                                        <span class="bp-screen-reader-text">
     37                                                                <?php
     38                                                                        /* translators: accessibility text */
     39                                                                        esc_html_e( 'Select this invitation', 'buddypress' );
     40                                                                ?>
     41                                                        </span>
     42                                                </label>
     43                                        </td>
     44                                        <td class="invitation-invitee"><?php bp_the_members_invitation_property( 'invitee_email' ); ?></td>
     45                                        <td class="invitation-content"><?php bp_the_members_invitation_property( 'content' ); ?></td>
     46                                        <td class="invitation-sent"><?php bp_the_members_invitation_property( 'invite_sent' ); ?></td>
     47                                        <td class="invitation-accepted"><?php bp_the_members_invitation_property( 'accepted' ); ?></td>
     48                                        <td class="invitation-date-modified"><?php bp_the_members_invitation_property( 'date_modified' ); ?></td>
     49                                        <td class="invitation-actions"><?php bp_the_members_invitation_action_links(); ?></td>
     50                                </tr>
     51
     52                        <?php endwhile; ?>
     53
     54                </tbody>
     55        </table>
     56
     57        <div class="invitations-options-nav">
     58                <?php // @TODO //bp_invitations_bulk_management_dropdown(); ?>
     59        </div><!-- .invitations-options-nav -->
     60
     61        <?php wp_nonce_field( 'invitations_bulk_nonce', 'invitations_bulk_nonce' ); ?>
     62</form>
  • new file src/bp-templates/bp-nouveau/buddypress/members/single/invitations/list-invites.php

    diff --git src/bp-templates/bp-nouveau/buddypress/members/single/invitations/list-invites.php src/bp-templates/bp-nouveau/buddypress/members/single/invitations/list-invites.php
    new file mode 100644
    index 000000000..92d92879f
    - +  
     1<?php
     2/**
     3 * BuddyPress - Pending Membership Invitations
     4 *
     5 * @since 8.0.0
     6 * @version 8.0.0
     7 */
     8?>
     9
     10<?php if ( bp_has_members_invitations() ) : ?>
     11
     12        <h2 class="bp-screen-reader-text">
     13                <?php
     14                /* translators: accessibility text */
     15                esc_html_e( 'Invitations', 'buddypress' );
     16                ?>
     17        </h2>
     18
     19        <div id="pag-top" class="pagination no-ajax">
     20                <div class="pag-count" id="invitations-count-top">
     21                        <?php bp_members_invitations_pagination_count(); ?>
     22                </div>
     23
     24                <div class="pagination-links" id="invitations-pag-top">
     25                        <?php bp_members_invitations_pagination_links(); ?>
     26                </div>
     27        </div>
     28
     29        <?php bp_get_template_part( 'members/single/invitations/invitations-loop' ); ?>
     30
     31        <div id="pag-bottom" class="pagination no-ajax">
     32                <div class="pag-count" id="invitations-count-bottom">
     33                        <?php bp_members_invitations_pagination_count(); ?>
     34                </div>
     35
     36                <div class="pagination-links" id="invitations-pag-bottom">
     37                        <?php bp_members_invitations_pagination_links(); ?>
     38                </div>
     39        </div>
     40
     41<?php else : ?>
     42
     43        <?php bp_nouveau_user_feedback( 'member-invites-none' ); ?>
     44
     45<?php endif;
  • new file src/bp-templates/bp-nouveau/buddypress/members/single/invitations/send-invites.php

    diff --git src/bp-templates/bp-nouveau/buddypress/members/single/invitations/send-invites.php src/bp-templates/bp-nouveau/buddypress/members/single/invitations/send-invites.php
    new file mode 100644
    index 000000000..077c1fa24
    - +  
     1<?php
     2/**
     3 * BuddyPress - Send a Membership Invitation.
     4 *
     5 * @since 8.0.0
     6 * @version 8.0.0
     7 */
     8?>
     9<h2 class="bp-screen-reader-text">
     10        <?php
     11        /* translators: accessibility text */
     12        esc_html_e( 'Send Invitation', 'buddypress' );
     13        ?>
     14</h2>
     15
     16<p class="bp-feedback info">
     17        <span class="bp-icon" aria-hidden="true"></span>
     18        <span class="bp-help-text">
     19                <?php esc_html_e( 'Fill out the form below to invite a new user to join this site. Upon submission of the form, an email will be sent to the invitee containing a link to accept your invitation. You may also add a custom message to the email.', 'buddypress' ); ?>
     20        </span>
     21</p>
     22
     23<form class="standard-form network-invitation-form" id="network-invitation-form" method="post">
     24        <label for="bp_members_invitation_invitee_email">
     25                <?php esc_html_e( 'Email', 'buddypress' ); ?>
     26                <span class="bp-required-field-label"><?php esc_html_e( '(required)', 'buddypress' ); ?></span>
     27        </label>
     28        <input id="bp_members_invitation_invitee_email" type="email" name="invitee_email" required="required">
     29
     30        <label for="bp_members_invitation_message">
     31                <?php esc_html_e( 'Add a personalized message to the invitation (optional)', 'buddypress' ); ?>
     32        </label>
     33        <textarea id="bp_members_invitation_message" name="invite_message"></textarea>
     34
     35        <input type="hidden" name="action" value="send-invite">
     36
     37        <?php bp_nouveau_submit_button( 'member-send-invite' ); ?>
     38</form>
  • 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 69a72e110..6a7bc097b 100644
    function bp_nouveau_theme_cover_image( $params = array() ) { 
    927927 * All user feedback messages are available here
    928928 *
    929929 * @since 3.0.0
     930 * @since 8.0.0 Adds the 'member-invites-none' feedback.
    930931 *
    931932 * @param string $feedback_id The ID of the message.
    932933 *
    function bp_nouveau_get_user_feedback( $feedback_id = '' ) { 
    939940         * Use this filter to add your custom feedback messages.
    940941         *
    941942         * @since 3.0.0
     943         * @since 8.0.0 Adds the 'member-invites-none' feedback.
    942944         *
    943945         * @param array $value The list of feedback messages.
    944946         */
    945         $feedback_messages = apply_filters( 'bp_nouveau_feedback_messages', array(
    946                 'registration-disabled' => array(
    947                         'type'    => 'info',
    948                         'message' => __( 'Member registration is currently not allowed.', 'buddypress' ),
    949                         'before'  => 'bp_before_registration_disabled',
    950                         'after'   => 'bp_after_registration_disabled'
    951                 ),
    952                 'request-details' => array(
    953                         'type'    => 'info',
    954                         'message' => __( 'Registering for this site is easy. Just fill in the fields below, and we\'ll get a new account set up for you in no time.', 'buddypress' ),
    955                         'before'  => false,
    956                         'after'   => false,
    957                 ),
    958                 'completed-confirmation' => array(
    959                         'type'    => 'info',
    960                         'message' => __( 'You have successfully created your account! Please log in using the username and password you have just created.', 'buddypress' ),
    961                         'before'  => 'bp_before_registration_confirmed',
    962                         'after'   => 'bp_after_registration_confirmed',
    963                 ),
    964                 'directory-activity-loading' => array(
    965                         'type'    => 'loading',
    966                         'message' => __( 'Loading the community updates. Please wait.', 'buddypress' ),
    967                 ),
    968                 'single-activity-loading' => array(
    969                         'type'    => 'loading',
    970                         'message' => __( 'Loading the update. Please wait.', 'buddypress' ),
    971                 ),
    972                 'activity-loop-none' => array(
    973                         'type'    => 'info',
    974                         'message' => __( 'Sorry, there was no activity found. Please try a different filter.', 'buddypress' ),
    975                 ),
    976                 'blogs-loop-none' => array(
    977                         'type'    => 'info',
    978                         'message' => __( 'Sorry, there were no sites found.', 'buddypress' ),
    979                 ),
    980                 'blogs-no-signup' => array(
    981                         'type'    => 'info',
    982                         'message' => __( 'Site registration is currently disabled.', 'buddypress' ),
    983                 ),
    984                 'directory-blogs-loading' => array(
    985                         'type'    => 'loading',
    986                         'message' => __( 'Loading the sites of the network. Please wait.', 'buddypress' ),
    987                 ),
    988                 'directory-groups-loading' => array(
    989                         'type'    => 'loading',
    990                         'message' => __( 'Loading the groups of the community. Please wait.', 'buddypress' ),
    991                 ),
    992                 'groups-loop-none' => array(
    993                         'type'    => 'info',
    994                         'message' => __( 'Sorry, there were no groups found.', 'buddypress' ),
    995                 ),
    996                 'group-activity-loading' => array(
    997                         'type'    => 'loading',
    998                         'message' => __( 'Loading the group updates. Please wait.', 'buddypress' ),
    999                 ),
    1000                 'group-members-loading' => array(
    1001                         'type'    => 'loading',
    1002                         'message' => __( 'Requesting the group members. Please wait.', 'buddypress' ),
    1003                 ),
    1004                 'group-members-none' => array(
    1005                         'type'    => 'info',
    1006                         'message' => __( 'Sorry, there were no group members found.', 'buddypress' ),
    1007                 ),
    1008                 'group-members-search-none' => array(
    1009                         'type'    => 'info',
    1010                         'message' => __( 'Sorry, there was no member of that name found in this group.', 'buddypress' ),
    1011                 ),
    1012                 'group-manage-members-none' => array(
    1013                         'type'    => 'info',
    1014                         'message' => __( 'This group has no members.', 'buddypress' ),
    1015                 ),
    1016                 'group-requests-none' => array(
    1017                         'type'    => 'info',
    1018                         'message' => __( 'There are no pending membership requests.', 'buddypress' ),
    1019                 ),
    1020                 'group-requests-loading' => array(
    1021                         'type'    => 'loading',
    1022                         'message' => __( 'Loading the members who requested to join the group. Please wait.', 'buddypress' ),
    1023                 ),
    1024                 'group-delete-warning' => array(
    1025                         'type'    => 'warning',
    1026                         'message' => __( 'WARNING: Deleting this group will completely remove ALL content associated with it. There is no way back. Please be careful with this option.', 'buddypress' ),
    1027                 ),
    1028                 'group-avatar-delete-info' => array(
    1029                         'type'    => 'info',
    1030                         'message' => __( 'If you\'d like to remove the existing group profile photo but not upload a new one, please use the delete group profile photo button.', 'buddypress' ),
    1031                 ),
    1032                 'directory-members-loading' => array(
    1033                         'type'    => 'loading',
    1034                         'message' => __( 'Loading the members of your community. Please wait.', 'buddypress' ),
    1035                 ),
    1036                 'members-loop-none' => array(
    1037                         'type'    => 'info',
    1038                         'message' => __( 'Sorry, no members were found.', 'buddypress' ),
    1039                 ),
    1040                 'member-requests-none' => array(
    1041                         'type'    => 'info',
    1042                         'message' => __( 'You have no pending friendship requests.', 'buddypress' ),
    1043                 ),
    1044                 'member-invites-none' => array(
    1045                         'type'    => 'info',
    1046                         'message' => __( 'You have no outstanding group invites.', 'buddypress' ),
    1047                 ),
    1048                 'member-notifications-none' => array(
    1049                         'type'    => 'info',
    1050                         'message' => __( 'This member has no notifications.', 'buddypress' ),
    1051                 ),
    1052                 'member-wp-profile-none' => array(
    1053                         'type'    => 'info',
    1054                         /* translators: %s: member name */
    1055                         'message' => __( '%s did not save any profile information yet.', 'buddypress' ),
    1056                 ),
    1057                 'member-delete-account' => array(
    1058                         'type'    => 'warning',
    1059                         'message' => __( 'Deleting this account will delete all of the content it has created. It will be completely unrecoverable.', 'buddypress' ),
    1060                 ),
    1061                 'member-activity-loading' => array(
    1062                         'type'    => 'loading',
    1063                         'message' => __( 'Loading the member\'s updates. Please wait.', 'buddypress' ),
    1064                 ),
    1065                 'member-blogs-loading' => array(
    1066                         'type'    => 'loading',
    1067                         'message' => __( 'Loading the member\'s blogs. Please wait.', 'buddypress' ),
    1068                 ),
    1069                 'member-friends-loading' => array(
    1070                         'type'    => 'loading',
    1071                         'message' => __( 'Loading the member\'s friends. Please wait.', 'buddypress' ),
    1072                 ),
    1073                 'member-groups-loading' => array(
    1074                         'type'    => 'loading',
    1075                         'message' => __( 'Loading the member\'s groups. Please wait.', 'buddypress' ),
    1076                 ),
    1077                 'member-notifications-loading' => array(
    1078                         'type'    => 'loading',
    1079                         'message' => __( 'Loading notifications. Please wait.', 'buddypress' ),
    1080                 ),
    1081                 'member-group-invites-all' => array(
    1082                         'type'    => 'info',
    1083                         'message' => __( 'Currently every member of the community can invite you to join their groups. If you are not comfortable with it, you can always restrict group invites to your friends only.', 'buddypress' ),
    1084                 ),
    1085                 'member-group-invites-friends-only' => array(
    1086                         'type'    => 'info',
    1087                         'message' => __( 'Currently only your friends can invite you to groups. Uncheck the box to allow any member to send invites.', 'buddypress' ),
    1088                 ),
    1089         ) );
     947        $feedback_messages = apply_filters(
     948                'bp_nouveau_feedback_messages',
     949                array(
     950                        'registration-disabled'             => array(
     951                                'type'    => 'info',
     952                                'message' => __( 'Member registration is currently not allowed.', 'buddypress' ),
     953                                'before'  => 'bp_before_registration_disabled',
     954                                'after'   => 'bp_after_registration_disabled'
     955                        ),
     956                        'request-details'                   => array(
     957                                'type'    => 'info',
     958                                'message' => __( 'Registering for this site is easy. Just fill in the fields below, and we\'ll get a new account set up for you in no time.', 'buddypress' ),
     959                                'before'  => false,
     960                                'after'   => false,
     961                        ),
     962                        'completed-confirmation'            => array(
     963                                'type'    => 'info',
     964                                'message' => __( 'You have successfully created your account! Please log in using the username and password you have just created.', 'buddypress' ),
     965                                'before'  => 'bp_before_registration_confirmed',
     966                                'after'   => 'bp_after_registration_confirmed',
     967                        ),
     968                        'directory-activity-loading'        => array(
     969                                'type'    => 'loading',
     970                                'message' => __( 'Loading the community updates. Please wait.', 'buddypress' ),
     971                        ),
     972                        'single-activity-loading'           => array(
     973                                'type'    => 'loading',
     974                                'message' => __( 'Loading the update. Please wait.', 'buddypress' ),
     975                        ),
     976                        'activity-loop-none'                => array(
     977                                'type'    => 'info',
     978                                'message' => __( 'Sorry, there was no activity found. Please try a different filter.', 'buddypress' ),
     979                        ),
     980                        'blogs-loop-none'                   => array(
     981                                'type'    => 'info',
     982                                'message' => __( 'Sorry, there were no sites found.', 'buddypress' ),
     983                        ),
     984                        'blogs-no-signup'                   => array(
     985                                'type'    => 'info',
     986                                'message' => __( 'Site registration is currently disabled.', 'buddypress' ),
     987                        ),
     988                        'directory-blogs-loading'           => array(
     989                                'type'    => 'loading',
     990                                'message' => __( 'Loading the sites of the network. Please wait.', 'buddypress' ),
     991                        ),
     992                        'directory-groups-loading'          => array(
     993                                'type'    => 'loading',
     994                                'message' => __( 'Loading the groups of the community. Please wait.', 'buddypress' ),
     995                        ),
     996                        'groups-loop-none'                  => array(
     997                                'type'    => 'info',
     998                                'message' => __( 'Sorry, there were no groups found.', 'buddypress' ),
     999                        ),
     1000                        'group-activity-loading'            => array(
     1001                                'type'    => 'loading',
     1002                                'message' => __( 'Loading the group updates. Please wait.', 'buddypress' ),
     1003                        ),
     1004                        'group-members-loading'             => array(
     1005                                'type'    => 'loading',
     1006                                'message' => __( 'Requesting the group members. Please wait.', 'buddypress' ),
     1007                        ),
     1008                        'group-members-none'                => array(
     1009                                'type'    => 'info',
     1010                                'message' => __( 'Sorry, there were no group members found.', 'buddypress' ),
     1011                        ),
     1012                        'group-members-search-none'         => array(
     1013                                'type'    => 'info',
     1014                                'message' => __( 'Sorry, there was no member of that name found in this group.', 'buddypress' ),
     1015                        ),
     1016                        'group-manage-members-none'         => array(
     1017                                'type'    => 'info',
     1018                                'message' => __( 'This group has no members.', 'buddypress' ),
     1019                        ),
     1020                        'group-requests-none'               => array(
     1021                                'type'    => 'info',
     1022                                'message' => __( 'There are no pending membership requests.', 'buddypress' ),
     1023                        ),
     1024                        'group-requests-loading'            => array(
     1025                                'type'    => 'loading',
     1026                                'message' => __( 'Loading the members who requested to join the group. Please wait.', 'buddypress' ),
     1027                        ),
     1028                        'group-delete-warning'              => array(
     1029                                'type'    => 'warning',
     1030                                'message' => __( 'WARNING: Deleting this group will completely remove ALL content associated with it. There is no way back. Please be careful with this option.', 'buddypress' ),
     1031                        ),
     1032                        'group-avatar-delete-info'          => array(
     1033                                'type'    => 'info',
     1034                                'message' => __( 'If you\'d like to remove the existing group profile photo but not upload a new one, please use the delete group profile photo button.', 'buddypress' ),
     1035                        ),
     1036                        'directory-members-loading'         => array(
     1037                                'type'    => 'loading',
     1038                                'message' => __( 'Loading the members of your community. Please wait.', 'buddypress' ),
     1039                        ),
     1040                        'members-loop-none'                 => array(
     1041                                'type'    => 'info',
     1042                                'message' => __( 'Sorry, no members were found.', 'buddypress' ),
     1043                        ),
     1044                        'member-requests-none'              => array(
     1045                                'type'    => 'info',
     1046                                'message' => __( 'You have no pending friendship requests.', 'buddypress' ),
     1047                        ),
     1048                        'member-invites-none'               => array(
     1049                                'type'    => 'info',
     1050                                'message' => __( 'You have no outstanding group invites.', 'buddypress' ),
     1051                        ),
     1052                        'member-notifications-none'         => array(
     1053                                'type'    => 'info',
     1054                                'message' => __( 'This member has no notifications.', 'buddypress' ),
     1055                        ),
     1056                        'member-wp-profile-none'            => array(
     1057                                'type'    => 'info',
     1058                                /* translators: %s: member name */
     1059                                'message' => __( '%s did not save any profile information yet.', 'buddypress' ),
     1060                        ),
     1061                        'member-delete-account'             => array(
     1062                                'type'    => 'warning',
     1063                                'message' => __( 'Deleting this account will delete all of the content it has created. It will be completely unrecoverable.', 'buddypress' ),
     1064                        ),
     1065                        'member-activity-loading'           => array(
     1066                                'type'    => 'loading',
     1067                                'message' => __( 'Loading the member\'s updates. Please wait.', 'buddypress' ),
     1068                        ),
     1069                        'member-blogs-loading'              => array(
     1070                                'type'    => 'loading',
     1071                                'message' => __( 'Loading the member\'s blogs. Please wait.', 'buddypress' ),
     1072                        ),
     1073                        'member-friends-loading'            => array(
     1074                                'type'    => 'loading',
     1075                                'message' => __( 'Loading the member\'s friends. Please wait.', 'buddypress' ),
     1076                        ),
     1077                        'member-groups-loading'             => array(
     1078                                'type'    => 'loading',
     1079                                'message' => __( 'Loading the member\'s groups. Please wait.', 'buddypress' ),
     1080                        ),
     1081                        'member-notifications-loading'      => array(
     1082                                'type'    => 'loading',
     1083                                'message' => __( 'Loading notifications. Please wait.', 'buddypress' ),
     1084                        ),
     1085                        'member-group-invites-all'          => array(
     1086                                'type'    => 'info',
     1087                                'message' => __( 'Currently every member of the community can invite you to join their groups. If you are not comfortable with it, you can always restrict group invites to your friends only.', 'buddypress' ),
     1088                        ),
     1089                        'member-group-invites-friends-only' => array(
     1090                                'type'    => 'info',
     1091                                'message' => __( 'Currently only your friends can invite you to groups. Uncheck the box to allow any member to send invites.', 'buddypress' ),
     1092                        ),
     1093                        'member-invites-none'               => array(
     1094                                'type'    => 'info',
     1095                                'message' => __( 'There are no invitations to display.', 'buddypress' ),
     1096                        ),
     1097                )
     1098        );
    10901099
    10911100        if ( ! isset( $feedback_messages[ $feedback_id ] ) ) {
    10921101                return false;
    function bp_nouveau_get_signup_fields( $section = '' ) { 
    12291238 * Get Some submit buttons data.
    12301239 *
    12311240 * @since 3.0.0
     1241 * @since 8.0.0 Adds the 'member-send-invite' button.
    12321242 *
    12331243 * @param string $action The action requested.
    12341244 *
    function bp_nouveau_get_submit_button( $action = '' ) { 
    12441254         * Filter the Submit buttons to add your own.
    12451255         *
    12461256         * @since 3.0.0
     1257         * @since 8.0.0 Adds the 'member-send-invite' button.
    12471258         *
    12481259         * @param array $value The list of submit buttons.
    12491260         *
    12501261         * @return array|false
    12511262         */
    1252         $actions = apply_filters( 'bp_nouveau_get_submit_button', array(
    1253                 'register' => array(
    1254                         'before'     => 'bp_before_registration_submit_buttons',
    1255                         'after'      => 'bp_after_registration_submit_buttons',
    1256                         'nonce'      => 'bp_new_signup',
    1257                         'attributes' => array(
    1258                                 'name'  => 'signup_submit',
    1259                                 'id'    => 'submit',
    1260                                 'value' => __( 'Complete Sign Up', 'buddypress' ),
     1263        $actions = apply_filters(
     1264                'bp_nouveau_get_submit_button',
     1265                array(
     1266                        'register'                      => array(
     1267                                'before'     => 'bp_before_registration_submit_buttons',
     1268                                'after'      => 'bp_after_registration_submit_buttons',
     1269                                'nonce'      => 'bp_new_signup',
     1270                                'attributes' => array(
     1271                                        'name'  => 'signup_submit',
     1272                                        'id'    => 'submit',
     1273                                        'value' => __( 'Complete Sign Up', 'buddypress' ),
     1274                                ),
    12611275                        ),
    1262                 ),
    1263                 'member-profile-edit' => array(
    1264                         'before' => '',
    1265                         'after'  => '',
    1266                         'nonce'  => 'bp_xprofile_edit',
    1267                         'attributes' => array(
    1268                                 'name'  => 'profile-group-edit-submit',
    1269                                 'id'    => 'profile-group-edit-submit',
    1270                                 'value' => __( 'Save Changes', 'buddypress' ),
     1276                        'member-profile-edit'           => array(
     1277                                'before'     => '',
     1278                                'after'      => '',
     1279                                'nonce'      => 'bp_xprofile_edit',
     1280                                'attributes' => array(
     1281                                        'name'  => 'profile-group-edit-submit',
     1282                                        'id'    => 'profile-group-edit-submit',
     1283                                        'value' => __( 'Save Changes', 'buddypress' ),
     1284                                ),
    12711285                        ),
    1272                 ),
    1273                 'member-capabilities' => array(
    1274                         'before' => 'bp_members_capabilities_account_before_submit',
    1275                         'after'  => 'bp_members_capabilities_account_after_submit',
    1276                         'nonce'  => 'capabilities',
    1277                         'attributes' => array(
    1278                                 'name'  => 'capabilities-submit',
    1279                                 'id'    => 'capabilities-submit',
    1280                                 'value' => __( 'Save', 'buddypress' ),
     1286                        'member-capabilities'           => array(
     1287                                'before'     => 'bp_members_capabilities_account_before_submit',
     1288                                'after'      => 'bp_members_capabilities_account_after_submit',
     1289                                'nonce'      => 'capabilities',
     1290                                'attributes' => array(
     1291                                        'name'  => 'capabilities-submit',
     1292                                        'id'    => 'capabilities-submit',
     1293                                        'value' => __( 'Save', 'buddypress' ),
     1294                                ),
    12811295                        ),
    1282                 ),
    1283                 'member-delete-account' => array(
    1284                         'before' => 'bp_members_delete_account_before_submit',
    1285                         'after'  => 'bp_members_delete_account_after_submit',
    1286                         'nonce'  => 'delete-account',
    1287                         'attributes' => array(
    1288                                 'disabled' => 'disabled',
    1289                                 'name'     => 'delete-account-button',
    1290                                 'id'       => 'delete-account-button',
    1291                                 'value'    => __( 'Delete Account', 'buddypress' ),
     1296                        'member-delete-account'         => array(
     1297                                'before'     => 'bp_members_delete_account_before_submit',
     1298                                'after'      => 'bp_members_delete_account_after_submit',
     1299                                'nonce'      => 'delete-account',
     1300                                'attributes' => array(
     1301                                        'disabled' => 'disabled',
     1302                                        'name'     => 'delete-account-button',
     1303                                        'id'       => 'delete-account-button',
     1304                                        'value'    => __( 'Delete Account', 'buddypress' ),
     1305                                ),
    12921306                        ),
    1293                 ),
    1294                 'members-general-settings' => array(
    1295                         'before' => 'bp_core_general_settings_before_submit',
    1296                         'after'  => 'bp_core_general_settings_after_submit',
    1297                         'nonce'  => 'bp_settings_general',
    1298                         'attributes' => array(
    1299                                 'name'  => 'submit',
    1300                                 'id'    => 'submit',
    1301                                 'value' => __( 'Save Changes', 'buddypress' ),
    1302                                 'class' => 'auto',
     1307                        'members-general-settings'      => array(
     1308                                'before'     => 'bp_core_general_settings_before_submit',
     1309                                'after'      => 'bp_core_general_settings_after_submit',
     1310                                'nonce'      => 'bp_settings_general',
     1311                                'attributes' => array(
     1312                                        'name'  => 'submit',
     1313                                        'id'    => 'submit',
     1314                                        'value' => __( 'Save Changes', 'buddypress' ),
     1315                                        'class' => 'auto',
     1316                                ),
    13031317                        ),
    1304                 ),
    1305                 'member-notifications-settings' => array(
    1306                         'before' => 'bp_members_notification_settings_before_submit',
    1307                         'after'  => 'bp_members_notification_settings_after_submit',
    1308                         'nonce'  => 'bp_settings_notifications',
    1309                         'attributes' => array(
    1310                                 'name'  => 'submit',
    1311                                 'id'    => 'submit',
    1312                                 'value' => __( 'Save Changes', 'buddypress' ),
    1313                                 'class' => 'auto',
     1318                        'member-notifications-settings' => array(
     1319                                'before'     => 'bp_members_notification_settings_before_submit',
     1320                                'after'      => 'bp_members_notification_settings_after_submit',
     1321                                'nonce'      => 'bp_settings_notifications',
     1322                                'attributes' => array(
     1323                                        'name'  => 'submit',
     1324                                        'id'    => 'submit',
     1325                                        'value' => __( 'Save Changes', 'buddypress' ),
     1326                                        'class' => 'auto',
     1327                                ),
    13141328                        ),
    1315                 ),
    1316                 'members-profile-settings' => array(
    1317                         'before' => 'bp_core_xprofile_settings_before_submit',
    1318                         'after'  => 'bp_core_xprofile_settings_after_submit',
    1319                         'nonce'  => 'bp_xprofile_settings',
    1320                         'attributes' => array(
    1321                                 'name'  => 'xprofile-settings-submit',
    1322                                 'id'    => 'submit',
    1323                                 'value' => __( 'Save Changes', 'buddypress' ),
    1324                                 'class' => 'auto',
     1329                        'members-profile-settings'      => array(
     1330                                'before'     => 'bp_core_xprofile_settings_before_submit',
     1331                                'after'      => 'bp_core_xprofile_settings_after_submit',
     1332                                'nonce'      => 'bp_xprofile_settings',
     1333                                'attributes' => array(
     1334                                        'name'  => 'xprofile-settings-submit',
     1335                                        'id'    => 'submit',
     1336                                        'value' => __( 'Save Changes', 'buddypress' ),
     1337                                        'class' => 'auto',
     1338                                ),
    13251339                        ),
    1326                 ),
    1327                 'member-group-invites' => array(
    1328                         'nonce'  => 'bp_nouveau_group_invites_settings',
    1329                         'attributes' => array(
    1330                                 'name'  => 'member-group-invites-submit',
    1331                                 'id'    => 'submit',
    1332                                 'value' => __( 'Save', 'buddypress' ),
    1333                                 'class' => 'auto',
     1340                        'member-group-invites'          => array(
     1341                                'nonce'      => 'bp_nouveau_group_invites_settings',
     1342                                'attributes' => array(
     1343                                        'name'  => 'member-group-invites-submit',
     1344                                        'id'    => 'submit',
     1345                                        'value' => __( 'Save', 'buddypress' ),
     1346                                        'class' => 'auto',
     1347                                ),
    13341348                        ),
    1335                 ),
    1336                 'activity-new-comment' => array(
    1337                         'after'     => 'bp_activity_entry_comments',
    1338                         'nonce'     => 'new_activity_comment',
    1339                         'nonce_key' => '_wpnonce_new_activity_comment',
    1340                         'wrapper'   => false,
    1341                         'attributes' => array(
    1342                                 'name'  => 'ac_form_submit',
    1343                                 'value' => _x( 'Post', 'button', 'buddypress' ),
     1349                        'member-send-invite'            => array(
     1350                                'nonce'                   => 'bp_members_invitation_send_%d',
     1351                                'nonce_placeholder_value' => bp_displayed_user_id() ? bp_displayed_user_id() : bp_loggedin_user_id(),
     1352                                'attributes'              => array(
     1353                                        'name'  => 'member-send-invite-submit',
     1354                                        'id'    => 'submit',
     1355                                        'value' => __( 'Send', 'buddypress' ),
     1356                                        'class' => 'auto',
     1357                                ),
    13441358                        ),
    1345                 ),
    1346         ) );
     1359                        'activity-new-comment'          => array(
     1360                                'after'      => 'bp_activity_entry_comments',
     1361                                'nonce'      => 'new_activity_comment',
     1362                                'nonce_key'  => '_wpnonce_new_activity_comment',
     1363                                'wrapper'    => false,
     1364                                'attributes' => array(
     1365                                        'name'  => 'ac_form_submit',
     1366                                        'value' => _x( 'Post', 'button', 'buddypress' ),
     1367                                ),
     1368                        ),
     1369                )
     1370        );
    13471371
    13481372        if ( isset( $actions[ $action ] ) ) {
    13491373                return $actions[ $action ];
  • src/bp-templates/bp-nouveau/includes/members/template-tags.php

    diff --git src/bp-templates/bp-nouveau/includes/members/template-tags.php src/bp-templates/bp-nouveau/includes/members/template-tags.php
    index b4fc3ae63..1c9385047 100644
    function bp_nouveau_member_template_part() { 
    660660                        $template = 'profile';
    661661                } elseif ( bp_is_user_notifications() ) {
    662662                        $template = 'notifications';
     663                } elseif ( bp_is_user_members_invitations() ) {
     664                        $template = 'invitations';
    663665                } elseif ( bp_is_user_settings() ) {
    664666                        $template = 'settings';
    665667                }
  • src/bp-templates/bp-nouveau/includes/template-tags.php

    diff --git src/bp-templates/bp-nouveau/includes/template-tags.php src/bp-templates/bp-nouveau/includes/template-tags.php
    index e195c22a5..c7bf7b702 100644
    function bp_nouveau_submit_button( $action, $object_id = 0 ) { 
    26412641                printf( '<div class="submit">%s</div>', $submit_input );
    26422642        }
    26432643
     2644        $nonce = $submit_data['nonce'];
     2645        if ( isset( $submit_data['nonce_placeholder_value'] ) ) {
     2646                $nonce = sprintf( $nonce, $submit_data['nonce_placeholder_value'] );
     2647        }
     2648
    26442649        if ( empty( $submit_data['nonce_key'] ) ) {
    2645                 wp_nonce_field( $submit_data['nonce'] );
     2650                wp_nonce_field( $nonce );
    26462651        } else {
    26472652                if ( $object_id ) {
    26482653                        $submit_data['nonce_key'] .= '_' . (int) $object_id;
    26492654                }
    26502655
    2651                 wp_nonce_field( $submit_data['nonce'], $submit_data['nonce_key'] );
     2656                wp_nonce_field( $nonce, $submit_data['nonce_key'] );
    26522657        }
    26532658
    26542659        if ( ! empty( $submit_data['after'] ) ) {
  • src/class-buddypress.php

    diff --git src/class-buddypress.php src/class-buddypress.php
    index 1af41585b..789cc418a 100644
    class BuddyPress { 
    622622                        'BP_REST_Attachments_Member_Avatar_Endpoint' => 'members',
    623623                        'BP_REST_Attachments_Member_Cover_Endpoint'  => 'members',
    624624                        'BP_REST_Signup_Endpoint'                    => 'members',
     625                        'BP_Members_Invitation_Manager'              => 'members',
     626                        'BP_Members_Invitations_Template'            => 'members',
    625627
    626628                        'BP_REST_Messages_Endpoint' => 'messages',
    627629