In this tutorial, we’ll learn about how we can┬áprogrammatically add custom fields to BuddyPress registration form, validate these fields and process them, and then show the user values in the user profile page.

If you haven’t already learned, BuddyPress acts like a WordPress multisite environment when it comes to handling signups. BuddyPress at first validates and processes the user submitted values in the signup, and adds a new signup to the wp_signups table, with an activation key. So, while a user is not activated, we can’t call it much of a user because yet it is not populated to the users table, and all the meta are kept in the signup meta field. A user then will have to check their email for an activation link to activate their membership, otherwise, in a delay of 2 days, the key can expire and the user will have to signup again. You can also manually activate signups through the users dashboard. The point here is, you’ll be extending the BuddyPress activation process to insert your custom fields, while they’re kept in the signup meta.

Registration and Processing

We can say this is the first part of this tutorial, which will address these main points:

  1. Filter the registration template to parse our custom fields.
  2. Validate these custom fields upon signup submission.
  3. Save the values into the signup user meta.
  4. Populate the values to the WP user meta upon user activation.

There are many many hooks and filters that BuddyPress ships with, most of them are documented in the internet but sometimes it is a bit hard to find the right filter or action hook, unless you do reverse-engineering which involves reading through the source code of BuddyPress itself, in aim to understand things better.

Embedding the field

We’re hooking into `bp_signup_profile_fields` action hook to embed our custom field to BuddyPress registration form. That’s an action hook so we’ll be printing out our HTML for this field. Oh and by the way, for the sake of simplicity, the custom field in subject will be a Twitter handle, to let users provide their Twitter usernames.

add_action('bp_signup_profile_fields', function() {
    global $twitter_field_value; ?>

    <div class="editfield field_twitter required-field visibility-public field_type_text" id="twitter-field">
        <label for="twitter"><?php _e('Twitter', 'my-plugin'); ?> 
            <span class="bp-required-field-label"><?php _e('(required)', 'my-plugin'); ?></span>
        </label>

        <?php do_action( 'bp_twitter_errors' ); ?>
        <input type="text" id="twitter" name="twitter" value="<?php echo esc_attr($twitter_field_value); ?>" />
    </div>

    <?php
});

The code is pretty much very basic, the value of the field is processed at a stage that comes next and stored into a global variable, which is used to retrieve the Twitter handle here for the input field.

Something important: in the `do_action` part, you must specify the tag as follows: `bp_{FIELDNAME}_errors`, so if we decided to reference our field as `twitter`, then `bp_twitter_errors`, whereas if `foobar`, then `bp_foobar_errors` etc. So that way the error messages are printed above the field, if any.

This is how it looks.

Validating the field

As you have already noticed, the Twitter field is marked as required, we’re after a required field here so let’s get into the process of how to validate it, and never register the user unless they provided their Twitter handles.

The hook this time is `bp_actions`, an action hook that is used a lot in BP core. It allows us to perform certain actions before the content is being rendered, and runs on many other pages and not only the registration page so we’ll use `bp_is_current_component` function to check if we are validating within the registration page and not somewhere else.

if ( $_POST ) {
    add_action('bp_actions', function() {
        global $bp, $twitter_field_value;

        // making sure we are in the registration page
        if ( !function_exists('bp_is_current_component') || !bp_is_current_component('register') )
            return;

        // validating our custom field
        if ( !isset($_POST['twitter']) || !trim($_POST['twitter']) ) {
            // we might be too early (priority 0), let's instantiate the errors array
            if ( !isset($bp->signup->errors) ) {
                $bp->signup->errors = array();
            }

            // now let's enter a custom error message
            // please make sure the errors index key is your custom field's name
            $bp->signup->errors['twitter'] = __('Please enter your Twitter handle', 'my-plugin');
        } else {
            $twitter_field_value = sanitize_text_field($_POST['twitter']);
        }
    }, 0);
}

We first check if the form is submitted (`$_POST` part), then we’re going to add a custom error message to `$bp->signup->errors[‘twitter’]` if something is wrong like the field value is not entered, make sure you replace `twitter` with your field name.

Let’s see a little preview of how this error is printed above the custom field.

bp register field error

Inserting field into signup meta

This time, we’ll want to hook into `bp_signup_usermeta` filter in order to add our custom field as signup meta. Since we’re already setting up the value of our field into a global variable, and since this filter runs after `bp_actions`, we can use that global variable to push our value.

add_filter('bp_signup_usermeta', function($usermeta){
    global $twitter_field_value;

    return array_merge(array('twitter' => $twitter_field_value), $usermeta);
});

Always remember to replace `twitter` with your field identifier.

So I did signup, and BuddyPress told me this “You have successfully created your account! To begin using this site you will need to activate your account via the email we have just sent to your address.”. Now to make sure everything’s correct, I checked the wp_signups table:

mysql> select * from wp_signups;
*************************** 1. row ***************************
     signup_id: 1
        domain: 
          path: 
         title: 
    user_login: samuel2
    user_email: [email protected]
    registered: 2017-10-26 09:01:14
     activated: 0000-00-00 00:00:00
        active: 0
activation_key: eRoywPA4H30peZL0Wzp6LsU2ptaOILtw
          meta: a:6:{s:7:"twitter";s:10:"samuel_elh";s:7:"field_1";s:10:"Samuel El.";s:18:"field_1_visibility";s:6:"public";s:18:"field_3_visibility";s:6:"public";s:17:"profile_field_ids";s:3:"1,3";s:8:"password";s:34:"$P$BBXYf6mLhz/3Tk0TNETFxUODZ7chfQ.";}
1 row in set (0.00 sec)

Yep, it’s `s:7:”twitter”;s:10:”samuel_elh”;` there in the meta. So far so good.

What is next? when the user clicks the activation link in the email, or when an admin automatically approves their signup, a new user is appended to the wp_users database table, and the user meta are inserted from the meta serialized value above. Right after that, you begin seeing the user in the WordPress dashboard users list, and can view their BuddyPress profile as well.

We’ll want to hook into the process where a user is created, the process explained above, so we can carry our meta into the actual user meta table. The filter `bp_core_activated_user` allows us to this exactly.

add_filter('bp_core_activated_user', function($user_id, $key, $user){
    $tag = 'twitter';

    if ( $user_id && !empty( $user['meta'][$tag] ) ) {
        return update_user_meta( $user_id, "bp_custom_{$tag}", $user['meta'][$tag] );
    }
}, 10, 3 );

Fair enough, I visited `http://bp.dev/activate/eRoywPA4H30peZL0Wzp6LsU2ptaOILtw/`, and then it said my account was successfully activated. I went ahead to check if the meta for the custom field is populated, and it was good news:

$ wp user meta list 8
+---------+-------------------------------+---------------------+
| user_id | meta_key                      | meta_value          |
+---------+-------------------------------+---------------------+
| 8       | nickname                      | Samuel El.          |
| 8       | first_name                    | Samuel              |
| 8       | last_name                     | El.                 |
| 8       | description                   |                     |
| 8       | rich_editing                  | true                |
| 8       | comment_shortcuts             | false               |
| 8       | admin_color                   | fresh               |
| 8       | use_ssl                       | 0                   |
| 8       | show_admin_bar_front          | true                |
| 8       | locale                        |                     |
| 8       | bp_xprofile_visibility_levels | {"1":"public"}      |
| 8       | wp_capabilities               | {"subscriber":true} |
| 8       | wp_user_level                 | 0                   |
| 8       | bp_custom_twitter             | samuel_elh          |
+---------+-------------------------------+---------------------+

It’s right there, in the last line where it says `bp_custom_twitter`. That is a user meta and can be retrieved or manipulated using user meta API.

So we’re pretty much done with the first part, which is also the critical one, let’s now dive into the last part.

Embedding to BuddyPress Profile

This the second and last part of this tutorial. We’ll make it a bit basic and quick as well, so the goal here is to embed this custom field into the user profile header, and also add it to the profile settings in case the user wants to change it, and so the old users will have the ability to enter this field value as well.

Embed field to BuddyPress profile header

We’ll go for an action hook named `bp_profile_header_meta` this time, which allows us to print more data into the profile header with all the other information available there.

add_action('bp_profile_header_meta', function(){
    $user_id = bp_displayed_user_id();

    $twitter_value = get_user_meta( $user_id, 'bp_custom_twitter', 1 );

    if ( $twitter_value ) {
        print '<p class="bbp-user-twitter-data">' . (
            sprintf( __( '@%s on Twitter', 'my-plugin' ), $twitter_value )
        ) . '</p>';
    }
});

`bp_displayed_user_id` will get you the currently displayed user ID, as there is nothing passed into this hook. We’ll use that to retrieve the custom user meta which stores our field (`bp_custom_{fieldname}`), and print an HTML markup for this.

Here’s how it looks:

buddypress profile header twitter field

Extending BuddyPress Profile Settings

BuddyPress profile settings section (/members/USERNAME/settings/) can be extended to add your custom fields. It’s quite easy to do so and validate and process these fields as well with all the available hooks and filers.

The hook `bp_core_general_settings_before_submit` allows us to populate HTML below all the other account settings fields.

add_action('bp_core_general_settings_before_submit', function() {
    $user_id = bp_displayed_user_id();

    ?>

    <div id="twitter-handle">
        <label for="twitter"><?php _e('Twitter handle', 'my-plugin'); ?></label>
        <input type="text" id="twitter" name="twitter" value="<?php echo esc_attr( get_user_meta( $user_id, 'bp_custom_twitter', 1 ) ); ?>" />
    </div>

    <?php

    // some nonce
    wp_nonce_field( 'twitter_nonce', 'twitter_nonce' );
});

buddypress profile settings twitter field

To process this newly added field, we’ll be hooking into `bp_core_general_settings_after_save` to check for form submission, validate a token first then process the custom field:

add_action('bp_core_general_settings_after_save', function() {
    if ( !isset($_POST['twitter_nonce'], $_POST['twitter']) || !wp_verify_nonce( $_POST['twitter_nonce'], 'twitter_nonce' ) )
        return;

    $user_id = bp_displayed_user_id();
    list($new_value, $old_value) = array( sanitize_text_field($_POST['twitter']), get_user_meta( $user_id, 'bp_custom_twitter', 1 ) );  

    if ( $new_value != $old_value ) {
        if ( $new_value ) {
            update_user_meta( $user_id, 'bp_custom_twitter', $new_value );
        } else {
            // perhaps you want to keep things required, so you might want to throw an error here
            // but with me, it's fine.
            delete_user_meta( $user_id, 'bp_custom_twitter' );
        }
        bp_core_add_message( __('Your Twitter handle was successfully updated.', 'my-plugin') . PHP_EOL, 'success');
    }
});

The above code will make the field optional, so that users can totally unset this field by submitting empty values. To disable that, just comment out the `delete_user_meta` part and add a custom error message at that stage.

Here’s a preview (apologise for the zoomed-out screen, I needed to make sure everything appears in the screenshot):

BuddyPress profile settings save twitter field

All right. I think we now have a custom user-defined value which we can use across the site, you can use this process to get more user information if you don’t want to use xprofile fields, or if you want to use this in your plugin.

Download the source code of this tutorial in a shape of a WordPress plugin.

Digital Ocean

Cheap Cloud SSD Hosting

Get a VPS now starting at $5/m, fast and perfect for WordPress and PHP applications

Sign Up with $10 Credit