How can I add an image field to BuddyPress Extended Profile Fields? [closed]

I figured out a way to go about adding the image type field without editing core BuddyPress files. The solution involves adding code to the theme’s functions.php that intercepts the plugin’s loading, display and saving. It also updates the WordPress upload location in order to direct the profile images to a subdirectory of the the uploads directory. The full solution is here.

Step 1.
Add a new field type:

function bpd_add_new_xprofile_field_type($field_types){
    $image_field_type = array('image');
    $field_types = array_merge($field_types, $image_field_type);
    return $field_types;
}

add_filter( 'xprofile_field_types', 'bpd_add_new_xprofile_field_type' );

Step 2.
Handle the rendering of the new field type in the WordPress Admin panel

function bpd_admin_render_new_xprofile_field_type($field, $echo = true){

    ob_start();
        switch ( $field->type ) {
            case 'image':
                ?>
                    <input type="file" name="<?php bp_the_profile_field_input_name() ?>" id="<?php bp_the_profile_field_input_name() ?>" value="" />
                <?php
                break;
            default :
                ?>
                    <p>Field type unrecognized</p>
                <?php
        }

        $output = ob_get_contents();
    ob_end_clean();

    if($echo){
        echo $output;
        return;
    }
    else{
        return $output;
    }

}

add_filter( 'xprofile_admin_field', 'bpd_admin_render_new_xprofile_field_type' );

Step 3.
Handle the rendering of the new field type on the WordPress front-end

function bpd_edit_render_new_xprofile_field($echo = true){

    if(empty ($echo)){
        $echo = true;
    }

    ob_start();
        if ( bp_get_the_profile_field_type() == 'image' ){
            $imageFieldInputName = bp_get_the_profile_field_input_name();
            $image = WP_CONTENT_URL . bp_get_the_profile_field_edit_value();

        ?>
                <label for="<?php bp_the_profile_field_input_name(); ?>"><?php bp_the_profile_field_name(); ?> <?php if ( bp_get_the_profile_field_is_required() ) : ?><?php _e( '(required)', 'buddypress' ); ?><?php endif; ?></label>
                <input type="file" name="<?php echo $imageFieldInputName; ?>" id="<?php echo $imageFieldInputName; ?>" value="" <?php if ( bp_get_the_profile_field_is_required() ) : ?>aria-required="true"<?php endif; ?>/>
                <img src="https://wordpress.stackexchange.com/questions/34163/<?php echo $image; ?>" alt="<?php bp_the_profile_field_name(); ?>" />

        <?php

        } 

        $output = ob_get_contents();
    ob_end_clean();

    if($echo){
        echo $output;
        return;
    }
    else{
        return $output;
    }

}

add_action( 'bp_custom_profile_edit_fields', 'bpd_edit_render_new_xprofile_field' );

Step 4.
Access the WordPress registered hook functions to remove the BuddyPress profile saving handler and insert a new one. This is necessary in order to handle the saving of the custom field before passing control back to BuddyPress.

function bpd_override_xprofile_screen_edit_profile(){
    $screen_edit_profile_priority = has_filter('bp_screens', 'xprofile_screen_edit_profile');

    if($screen_edit_profile_priority !== false){
        //Remove the default profile_edit handler
        remove_action( 'bp_screens', 'xprofile_screen_edit_profile', $screen_edit_profile_priority );

        //Install replalcement hook
        add_action( 'bp_screens', 'bpd_screen_edit_profile', $screen_edit_profile_priority );
    }
}

add_action( 'bp_actions', 'bpd_override_xprofile_screen_edit_profile', 10 );

Step 5.
Create the custom field saving function

function bpd_screen_edit_profile(){

    if ( isset( $_POST['field_ids'] ) ) {
        if(wp_verify_nonce( $_POST['_wpnonce'], 'bp_xprofile_edit' )){

            $posted_field_ids = explode( ',', $_POST['field_ids'] );

            $post_action_found = false;
            $post_action = '';
            if (isset($_POST['action'])){
                $post_action_found = true;
                $post_action = $_POST['action'];

            }

            foreach ( (array)$posted_field_ids as $field_id ) {
                $field_name="field_" . $field_id;

                if ( isset( $_FILES[$field_name] ) ) {
                    require_once( ABSPATH . '/wp-admin/includes/file.php' );
                    $uploaded_file = $_FILES[$field_name]['tmp_name'];

                    // Filter the upload location
                    add_filter( 'upload_dir', 'bpd_profile_upload_dir', 10, 1 );

                    //ensure WP accepts the upload job
                    $_POST['action'] = 'wp_handle_upload';

                    $uploaded_file = wp_handle_upload( $_FILES[$field_name] );

                    $uploaded_file = str_replace(WP_CONTENT_URL, '', $uploaded_file['url']) ;

                    $_POST[$field_name] = $uploaded_file;

                }
            }

            if($post_action_found){
                $_POST['action'] = $post_action;
            }
            else{
                unset($_POST['action']);
            }

        }
    }

    if(!defined('DOING_AJAX')){
        if(function_exists('xprofile_screen_edit_profile')){
            xprofile_screen_edit_profile();
        }
    }

}

Step 6.
Override the WordPress upload directory location to provide a custom image saving location

function bpd_profile_upload_dir( $upload_dir ) {
    global $bp;

    $user_id = $bp->displayed_user->id;
    $profile_subdir="/profiles/" . $user_id;

    $upload_dir['path'] = $upload_dir['basedir'] . $profile_subdir;
    $upload_dir['url'] = $upload_dir['baseurl'] . $profile_subdir;
    $upload_dir['subdir'] = $profile_subdir;

    return $upload_dir;
}

Step 7.
Create a javascript file that shall hold code for updating the field type selection drop-down to insert the new field type when the profile front-end is displayed. Let’s call the file xprofile-image.js and save it in the same location as the theme’s functions.php

(

    function(jQ){
        //outerHTML method (http://stackoverflow.com/a/5259788/212076)
        jQ.fn.outerHTML = function() {
            $t = jQ(this);
            if( "outerHTML" in $t[0] ){
                return $t[0].outerHTML;
            }
            else
            {
                var content = $t.wrap('<div></div>').parent().html();
                $t.unwrap();
                return content;
            }
        }

        bpd =
        {

        init : function(){

                //add image field type on Add/Edit Xprofile field admin screen
               if(jQ("div#poststuff select#fieldtype").html() !== null){

                    if(jQ('div#poststuff select#fieldtype option[value="image"]').html() === null){
                        var imageOption = '<option value="image">Image</option>';
                        jQ("div#poststuff select#fieldtype").append(imageOption);

                        var selectedOption = jQ("div#poststuff select#fieldtype").find("option:selected");
                        if((selectedOption.length == 0) || (selectedOption.outerHTML().search(/selected/i) < 0)){
                            var action = jQ("div#poststuff").parent().attr("action");

                            if (action.search(/mode=edit_field/i) >= 0){
                                jQ('div#poststuff select#fieldtype option[value="image"]').attr("selected", "selected");
                            }
                        }
                    }

                }

            }
        };

        jQ(document).ready(function(){
                bpd.init();
        });

    }

)(jQuery);

Step 8.
Load the js file (xprofile-image.js)

function bpd_load_js() {
     wp_enqueue_script( 'bpd-js', get_bloginfo('stylesheet_directory') . '/xprofile-image.js',
                            array( 'jquery' ), '1.0' );
}

add_action( 'wp_print_scripts', 'bpd_load_js' );

That’s it! The WordPress user profile now has support for additional image fields via the BuddyPress extended profile component.

This would be really helpful if it could be turned into a plugin.

UPDATE:
I got around to creating the plugin and it’s available on the WordPress Plugins page: