Skip to content

Instantly share code, notes, and snippets.

@sbrajesh
Last active February 3, 2020 11:59
Show Gist options
  • Star 1 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save sbrajesh/238153f70f770f957fd1c4b2ba60be77 to your computer and use it in GitHub Desktop.
Save sbrajesh/238153f70f770f957fd1c4b2ba60be77 to your computer and use it in GitHub Desktop.
Specific use case for Herve for profile completion.
class BuddyDev_Profile_Field_Completion_Helper {
/**
* Which member type should be set? PLease change it.
*
* @var string
*/
private $member_type = 'YOUR_MEMBER_TYPE'; // Please Change it.
/**
* Should this member type be appended to users existing member types or replace?
* Set true to append.
*
* @var bool
*/
private $append_member_type = false;
/**
* Show notice to user?
*
* @var bool
*/
private $show_notice = true;
/**
* Please do not set it. It ise used for temporary notice.
*
* @var string
*/
private $notice = '';
/**
* Should we restrict user when they have incomplete profile?
* It will force redirect user to complete their profile if enabled.
*
* @var bool
*/
private $restrict_on_incomplete = true;
/**
* require profile photo to decide the profile completion.
*
* @var bool
*/
private $require_photo = false;
/**
* require profile field completion.
*
* @var bool
*/
private $require_fields = true;
/**
* Notice messages based on required field or photo.
*
* @var array
*/
private $notices = array(
'photo' => 'Please upload your profile photo!',
'fields' => 'Please complete your profile.'
);
/**
* Constructor.
*/
public function __construct() {
add_filter( 'bp_settings_admin_nav', '__return_empty_array' );
$this->setup();
}
/**
* Setup hooks.
*/
public function setup() {
// change member type when the profile completed(in the sense we want) is triggered.
add_action( 'buddydev_bp_user_profile_completed', array( $this, 'set_custom_member_type' ) );
// check on login for the profile completion.
add_action( 'wp_login', array( $this, 'on_login_check' ), 0 );
// check on account activation for the profile complete.
add_action( 'bp_core_activated_user', array( $this, 'check_on_update' ) );
// check on profile update for the profile completion.
add_action( 'xprofile_updated_profile', array( $this, 'check_on_update' ), 0 );
// ON AVATAR UPLOAD
//on avatar delete
// record on new avatar upload & check for profile completion
add_action( 'xprofile_avatar_uploaded', array( $this, 'log_uploaded' ) );
// on avatar delete, remove the log and mark profile incomplete.
add_action( 'bp_core_delete_existing_avatar', array( $this, 'log_deleted' ) );
// Show teh notice.
add_action( 'bp_template_redirect', array( $this, 'check_profile_completion_state' ) );
}
/**
* Check profile completion on login.
*
* @param string $user_login username.
*/
public function on_login_check( $user_login ) {
$user = get_user_by( 'login', $user_login );
if ( ! $user ) {
return;
}
$this->check_on_update( $user->ID );
}
/**
* Checks for profile update and triggers profile completed action.
*
* @param int $user_id user id.
*/
public function check_on_update( $user_id ) {
$this->show_notice_and_redirect( $user_id );
}
/**
* On New Avatar Upload, add the user meta to reflect that user has uploaded an avatar
*
* @param int $user_id user whose avatar changed.
*/
public function log_uploaded( $user_id ) {
bp_update_user_meta( $user_id, 'has_avatar', 1 );
$this->check_on_update( $user_id );
}
/**
* On Delete Avatar, delete the user meta to reflect the change
*
* @param array $args see args array.
*/
public function log_deleted( $args ) {
if ( $args['object'] != 'user' ) {
return;
}
$user_id = empty( $args['item_id'] ) ? 0 : absint( $args['item_id'] );
if ( ! $user_id ) {
if ( bp_is_user() && ( bp_is_my_profile() || is_super_admin() ) ) {
$user_id = bp_displayed_user_id();
} else {
$user_id = bp_loggedin_user_id();
}
}
// we are sure it was user avatar delete
// remove the log from user meta.
bp_delete_user_meta( $user_id, 'has_avatar' );
$this->mark_incomplete_profile( $user_id );
}
/**
* Set custom member type if the profile just completed.
*
* @param int $user_id user id.
*/
public function set_custom_member_type( $user_id ) {
bp_set_member_type( $user_id, $this->member_type, $this->append_member_type );
}
/**
* Check's user's profile completion state.
*/
public function check_profile_completion_state() {
if ( ! is_user_logged_in() ) {
return;
}
$this->show_notice_and_redirect( bp_loggedin_user_id() );
}
/**
* Check for profile completion and add notice.
*
* @param int $user_id user id.
*/
private function show_notice_and_redirect( $user_id ) {
if ( ! $this->has_incomplete_profile( $user_id ) ) {
// the profile is complete.
return;
}
if ( is_super_admin( $user_id ) ) {
return;// no need to force super admin.
}
if ( apply_filters( 'bp_force_profile_completion_skip_check', false ) ) {
return ;
}
$incomplete = true;
// consider that we have the has_field and avatar available by default.
$has_fields = $has_photo = true;
// check if fields are required
if ( $this->require_fields ) {
$has_fields = $this->has_required_field_data( $user_id );
}
if ( $this->require_photo ) {
$has_photo = $this->has_uploaded_avatar( $user_id );
}
$redirect_url = bp_core_get_user_domain( $user_id ) . bp_get_profile_slug();
// this might have happened magically(most probably someone update profile by code).
if ( $has_photo && $has_fields ) {
$this->mark_complete_profile( $user_id );
$incomplete = false;
do_action( 'buddydev_bp_user_profile_completed', $user_id );
} elseif ( ! $has_fields ) {
$this->notice = $this->notices['fields'];
$redirect_url = $redirect_url . '/edit/';
} else {
$this->notice = $this->notices['photo'];
$redirect_url = $redirect_url . '/change-avatar/';
}
if ( $incomplete ) {
if ( $this->show_notice && $this->notice ) {
bp_core_add_message( $this->notice, 'error' );
}
if ( $this->restrict_on_incomplete && ! bp_is_user_profile() ) {
bp_core_redirect( $redirect_url );
}
}
}
/**
* Mark profile incomplete.
*
* @param int $user_id user id.
*
* @return bool
*/
public function has_incomplete_profile( $user_id ) {
if ( get_user_meta( $user_id, '_has_complete_profile', true ) ) {
return false;
}
return true;
}
/**
* Mark profile as incomplete.
*
* @param int $user_id user id.
*
* @return bool
*/
public function mark_incomplete_profile( $user_id ) {
return delete_user_meta( $user_id, '_has_complete_profile' );
}
/**
* Check if user has incomplete profile field data.
*
* @param int $user_id user id.
*
* @return bool|int
*/
public function mark_complete_profile( $user_id ) {
return update_user_meta( $user_id, '_has_complete_profile', 1 );
}
public function has_uploaded_avatar( $user_id ) {
// assuming that you are using force profile photo plugin that allows tracking of the user avatar.
$has_avatar = bp_get_user_meta( $user_id, 'has_avatar', true );
if ( ! $has_avatar ) {
$has_avatar = bp_get_user_has_avatar( $user_id );// fallback.
}
return $has_avatar;
}
/**
* Check if all required fields is complete.
*
* @param $user_id
*
* @return bool|mixed
*/
public function has_required_field_data( $user_id ) {
global $wpdb;
$has_fields_complete = get_user_meta( $user_id, '_has_required_field_data', true );
if ( $has_fields_complete ) {
return $has_fields_complete; // no need to test further.
}
$required_fields = $this->get_all_required_profile_fields();
// no required field, so profile should be considered complete
if ( empty( $required_fields ) ) {
// update meta
update_user_meta( $user_id, '_has_required_field_data', 1 );
return true;
}
$fields_list = '(' . join( ',', $required_fields ) . ' )';
$table = buddypress()->profile->table_name_data;
$query = "SELECT field_id, value FROM {$table} WHERE user_id = %d AND field_id IN {$fields_list}";
$profile_entries = $wpdb->get_results( $wpdb->prepare( $query, $user_id ) );
$complete = false;
// not all fields are set.
// shortcut.
if ( count( $profile_entries ) != count( $required_fields ) ) {
// unset flag.
delete_user_meta( $user_id, '_has_required_field_data' );
} else {
$complete = true;
// it should be complete but is the value acually not empty?
// check by unserializing field values
foreach ( $profile_entries as $profile_entry ) {
$value = maybe_unserialize( $profile_entry->value );
if ( empty( $value ) ) {
$complete = false;
break;
}
}
if ( $complete ) {
update_user_meta( $user_id, '_has_required_field_data', 1 );
}
}
return $complete;
}
/**
* Get all required field ids.
*
* @return array
*/
public function get_all_required_profile_fields() {
global $wpdb;
$table = buddypress()->profile->table_name_fields;
$query = "SELECT id FROM {$table} WHERE is_required = %d";
$fields = $wpdb->get_col( $wpdb->prepare( $query, 1 ) );
return $fields;
}
}
new BuddyDev_Profile_Field_Completion_Helper();
/**
* Do not redirect users on non bp pages while using BP Profile completion test.
*
* @param bool $skip should we skip or redirect.
*
* @return bool
*/
function buddydev_skip_check_for_profile_completion( $skip ) {
if ( ! is_buddypress() ) {
$skip = true;
}
return $skip;
}
add_filter( 'bp_force_profile_completion_skip_check', 'buddydev_skip_check_for_profile_completion' );
@HDInfautre
Copy link

HDInfautre commented Nov 30, 2017

Hello Brajesh,

Wow :-) , I did not expect such a complete program!!

Some questions:
1/ if there will be no conflict using the BP force profile plugin at the same time?

It's a pretty different programming. To avoid breaking the code, I want to know where to put my codes

2/ where put in variable of the type of member (not to forget it and ... no problem):

$member_type_unique_name='membre0'; // ??????? OK ????????****
class BuddyDev_Profile_Field_Completion_Helper {
...
public function set_custom_member_type( $user_id ) {
		$append = false; // set it to true if you just want to append this member type.
		bp_set_member_type( $user_id, $member_type_unique_name, $append ); 
	}
...

3/ when I switch from member type, I have to switch also with the armember plugin:

	public function check_on_update( $user_id ) {
		if ( $this->has_incomplete_profile( $user_id ) && $this->has_required_field_data( $user_id ) && $this->has_uploaded_avatar( $user_id ) ) {
			$this->mark_complete_profile( $user_id );
			do_action( 'buddydev_bp_user_profile_completed', $user_id );
do_action( 'arm_apply_plan_to_member', 2, $user_id);// ??????? OK ????????****
		}
	}

4/ where put my message for mandatory fields not entered:
public function has_required_field_data( $user_id ) {
...

// not all fields are set.
		if ( $count != count( $required_fields ) ) {
			// unset flag.
			delete_user_meta( $user_id, '_has_required_field_data' );

***********// ??????? OK ????????

              $message = '[su_shadow style="horizontal"][su_note note_color="#ff6668" radius="5"]' .'(' . $xprofile_fields_count . __(' donnée(s) manquante(s)', 'bp-force-profile') . __('). Merci de compléter votre profil pour 						 devenir membre et accéder à toutes les fonctionnalités du site.', 'bp-force-profile') .'<br />Sauvegardez avant de passer à un autre onglet.';
               $message .= '<ul">';
               /*if (empty($picture)) {
                   $message .= '<li>Photo de profil</li>'; Texte de la note
               }*/
               if ($xprofile_fields_count > 0) {
                   foreach ($xprofile_fields as $field) {
                   	 $message .= '<li>' . $field->field_name . ' (Onglet: ' .$field->group_name . ') </li>';
                   }
               }
               $message .= '</ul>';                
               echo  $message . '[/su_note][/su_shadow]';

Regards

@sbrajesh
Copy link
Author

sbrajesh commented Dec 7, 2017

I have updated the gist with the placeholders now.

@HDInfautre
Copy link

HDInfautre commented Jan 8, 2018

`new BuddyDev_Profile_Field_Completion_Helper();
class BuddyDev_Profile_Field_Completion_Helper {

/**
 * Which member type should be set? PLease change it.
 *
 * @var string
 */
private $member_type = 'membre0'; // Membre de base

/**
 * Ce type de membre doit-il être ajouté aux types de membre existants des utilisateurs ou remplacer? * Définissez true pour ajouter.
 *
 * @var bool
 */
private $append_member_type = false;

/**
 * Show notice to user?
 *
 * @var bool
 */
private $show_notice = true;

/**
 * Please do not set it. It ise used for temporary notice.
 *
 * @var string
 */
private $notice = '';


/**
 * Should we restrict user when they have incomplete profile?
 * It will force redirect user to complete their profile if enabled.
 *
 * @var bool
 */
private $restrict_on_incomplete = false; 

/**
 * require profile photo to decide the profile completion.
 *
 * @var bool
 */
private $require_photo = true;

/**
 * require profile field completion.
 *
 * @var bool
 */
private $require_fields = true;

/**
 * Notice messages based on required field or photo.
 *
 * @var array
 */
private $notices = array(
	'photo'  => 'Merci ajouter une photo de profil!!',
	'fields' => 'Merci de compléter votre profil.'
);

/**
 * Constructor.
 */
public function __construct() {
	add_filter( 'bp_settings_admin_nav', '__return_empty_array' );
	$this->setup();
}

/**
 * Setup hooks.
 */
public function setup() {
	// change member type when the profile completed(in the sense we want) is triggered.
	add_action( 'buddydev_bp_user_profile_completed', array( $this, 'set_custom_member_type' ) );

	// check on login for the profile completion.
	add_action( 'wp_login', array( $this, 'on_login_check' ), 0 );

	// check on account activation for the profile complete.
	add_action( 'bp_core_activated_user', array( $this, 'check_on_update' ) );
	// check on profile update for the profile completion.
	add_action( 'xprofile_updated_profile', array( $this, 'check_on_update' ), 0 );
	// ON AVATAR UPLOAD
	//on avatar delete

	// record on new avatar upload & check for profile completion
	add_action( 'xprofile_avatar_uploaded', array( $this, 'log_uploaded' ) );
	// on avatar delete, remove the log and mark profile incomplete.
	add_action( 'bp_core_delete_existing_avatar', array( $this, 'log_deleted' ) );
	// Show teh notice.
	add_action( 'bp_template_redirect', array( $this, 'check_profile_completion_state' ) );
}

/**
 * Check profile completion on login.
 *
 * @param string $user_login username.
 */
public function on_login_check( $user_login ) {

	$user = get_user_by( 'login', $user_login );
	if ( ! $user ) {
		return;
	}
	$this->check_on_update( $user->ID );
}

/**
 * Checks for profile update and triggers profile completed action.
 *
 * @param int $user_id user id.
 */
public function check_on_update( $user_id ) {

	$this->show_notice_and_redirect( $user_id );
}

/**
 * On New Avatar Upload, add the user meta to reflect that user has uploaded an avatar
 *
 * @param int $user_id user whose avatar changed.
 */
public function log_uploaded( $user_id ) {
	bp_update_user_meta( $user_id, 'has_avatar', 1 );
	$this->check_on_update( $user_id );
}

/**
 * On Delete Avatar, delete the user meta to reflect the change
 *
 * @param array $args see args array.
 */
public function log_deleted( $args ) {

	if ( $args['object'] != 'user' ) {
		return;
	}

	$user_id = empty( $args['item_id'] ) ? 0 : absint( $args['item_id'] );

	if ( ! $user_id ) {
		if ( bp_is_user() && ( bp_is_my_profile() || is_super_admin() ) ) {
			$user_id = bp_displayed_user_id();
		} else {
			$user_id = bp_loggedin_user_id();
		}
	}

	// we are sure it was user avatar delete
	// remove the log from user meta.
	bp_delete_user_meta( $user_id, 'has_avatar' );
	$this->mark_incomplete_profile( $user_id );
}

/**
 * Set custom member type if the profile just completed.
 *
 * @param int $user_id user id.
 */
public function set_custom_member_type( $user_id ) {
	bp_set_member_type( $user_id, $this->member_type, $this->append_member_type );
}

/**
 * Check's user's profile completion state.
 */
public function check_profile_completion_state() {
	if ( ! is_user_logged_in() ) {
		return;
	}

	$this->show_notice_and_redirect( bp_loggedin_user_id() );
}

/**
 * Check for profile completion and add notice.
 *
 * @param int $user_id user id.
 */
private function show_notice_and_redirect( $user_id ) {

	if ( ! $this->has_incomplete_profile( $user_id ) ) {
		// the profile is complete.
		return;
	}

	if ( is_super_admin( $user_id ) ) {
		return;// no need to force super admin.
	}
	if ( apply_filters( 'bp_force_profile_completion_skip_check', false ) ) {
	    return ;
    }

	$incomplete = true;
	// consider that we have the has_field and avatar available by default.
	$has_fields = $has_photo = true;

	// check if fields are required
	if ( $this->require_fields ) {
		$has_fields = $this->has_required_field_data( $user_id );
	}
	

	if ( $this->require_photo ) {
		$has_photo = $this->has_uploaded_avatar( $user_id );
	}

	$redirect_url = bp_core_get_user_domain( $user_id ) . bp_get_profile_slug();

	// this might have happened magically(most probably someone update profile by code).
	if ( $has_photo && $has_fields ) {
		$this->mark_complete_profile( $user_id );
		$incomplete = false;
		do_action( 'buddydev_bp_user_profile_completed', $user_id );
		do_action( 'arm_apply_plan_to_member', 2, $user_id); // 10/01/18: HD AJOUT pour rejoindre plan de base (& role: membre0)
	} elseif ( ! $has_fields ) {
		$this->notice = $this->notices['fields'];
		$redirect_url = $redirect_url . '/edit/';
	} else {
		$this->notice = $this->notices['photo'];
		$redirect_url = $redirect_url . '/change-avatar/';
	}

	if ( $incomplete ) {
		if ( $this->show_notice && $this->notice ) {
			bp_core_add_message( $this->notice, 'error' );
		}

		if ( $this->restrict_on_incomplete && ! bp_is_user_profile() ) {
			bp_core_redirect( $redirect_url );
		}
	}

}

/**
 * Mark profile incomplete.
 *
 * @param int $user_id user id.
 *
 * @return bool
 */
public function has_incomplete_profile( $user_id ) {

	if ( get_user_meta( $user_id, '_has_complete_profile', true ) ) {
		return false;
	}

	return true;
}

/**
 * Mark profile as incomplete.
 *
 * @param int $user_id user id.
 *
 * @return bool
 */
public function mark_incomplete_profile( $user_id ) {
	do_action( 'arm_apply_plan_to_member', 1, $user_id); // 10/01/18: HD AJOUT pour rejoindre plan sans profil finalisé (& role: subscriber)
	return delete_user_meta( $user_id, '_has_complete_profile' );
}

/**
 * Check if user has incomplete profile field data.
 *
 * @param int $user_id user id.
 *
 * @return bool|int
 */
public function mark_complete_profile( $user_id ) {
	return update_user_meta( $user_id, '_has_complete_profile', 1 );
}

public function has_uploaded_avatar( $user_id ) {
	// assuming that you are using force profile photo plugin that allows tracking of the user avatar.
	$has_avatar = bp_get_user_meta( $user_id, 'has_avatar', true );

	if ( ! $has_avatar ) {
		$has_avatar = bp_get_user_has_avatar( $user_id );// fallback.
	}

	return $has_avatar;
}

/**
 * Check if all required fields is complete.
 *
 * @param $user_id
 *
 * @return bool|mixed
 */
public function has_required_field_data( $user_id ) {
	global $wpdb;

	$has_fields_complete = get_user_meta( $user_id, '_has_required_field_data', true );
	if ( $has_fields_complete ) {
		return $has_fields_complete; // no need to test further.
	}

	$required_fields = $this->get_all_required_profile_fields();
	// no required field, so profile should be considered complete
	if ( empty( $required_fields ) ) {
		// update meta
		update_user_meta( $user_id, '_has_required_field_data', 1 );

		return true;
	}

	$fields_list = '(' . join( ',', $required_fields ) . ' )';

	$table = buddypress()->profile->table_name_data;

	$query = "SELECT field_id, value  FROM {$table} WHERE user_id = %d AND field_id IN {$fields_list}";

	$profile_entries = $wpdb->get_results( $wpdb->prepare( $query, $user_id ) );

	$complete = false;
	// not all fields are set.
	// shortcut.
	if ( count( $profile_entries ) != count( $required_fields ) ) {
		// unset flag.
		delete_user_meta( $user_id, '_has_required_field_data' );
	} else {
		$complete = true;
		// it should be complete but is the value acually not empty?
		// check by unserializing field values
		foreach ( $profile_entries as $profile_entry ) {
			$value = maybe_unserialize( $profile_entry->value );
			if ( empty( $value ) ) {
				$complete = false;
				break;
			}
		}
		if ( $complete ) {
			update_user_meta( $user_id, '_has_required_field_data', 1 );
		}
	}

	return $complete;
}

/**
 * Get all required field ids.
 *
 * @return array
 */
public function get_all_required_profile_fields() {

	global $wpdb;

	$table = buddypress()->profile->table_name_fields;

	$query = "SELECT id FROM {$table} WHERE is_required = %d";

	$fields = $wpdb->get_col( $wpdb->prepare( $query, 1 ) );

	return $fields;
}

}`

@HDInfautre
Copy link

Hello,
It works pretty well :-) but I just realized that if a person removes his picture, it does not change role and plan !.
Where ? do I have to put a piece of code ? to go back to the previous plan and role of subscriber?
Regards

@HDInfautre
Copy link

HDInfautre commented Mar 16, 2018

` new BuddyDev_Profile_Field_Completion_Helper();
class BuddyDev_Profile_Field_Completion_Helper {

/**
 * Which member type should be set? PLease change it.
 *
 * @var string
 */
private $member_type = 'membre0'; // Membre de base

/**
 * Ce type de membre doit-il être ajouté aux types de membre existants des utilisateurs ou remplacer? * Définissez true pour ajouter.
 *
 * @var bool
 */
private $append_member_type = false;

/**
 * Show notice to user?
 *
 * @var bool
 */
private $show_notice = true;

/**
 * Please do not set it. It ise used for temporary notice.
 *
 * @var string
 */
private $notice = '';


/**
 * Should we restrict user when they have incomplete profile?
 * It will force redirect user to complete their profile if enabled.
 *
 * @var bool
 */
private $restrict_on_incomplete = false; 

/**
 * require profile photo to decide the profile completion.
 *
 * @var bool
 */
private $require_photo = true;

/**
 * require profile field completion.
 *
 * @var bool
 */
private $require_fields = true;

/**
 * Notice messages based on required field or photo.
 *
 * @var array
 */
private $notices = array(
	'photo'  => 'Merci ajouter une photo de profil!!',
	'fields' => 'Merci de compléter votre profil.'
);

/**
 * Constructor.
 */
public function __construct() {
	add_filter( 'bp_settings_admin_nav', '__return_empty_array' );
	$this->setup();
}

/**
 * Setup hooks.
 */
public function setup() {
	// change member type when the profile completed(in the sense we want) is triggered.
	add_action( 'buddydev_bp_user_profile_completed', array( $this, 'set_custom_member_type' ) );

	// check on login for the profile completion.
	add_action( 'wp_login', array( $this, 'on_login_check' ), 0 );

	// check on account activation for the profile complete.
	add_action( 'bp_core_activated_user', array( $this, 'check_on_update' ) );
	// check on profile update for the profile completion.
	add_action( 'xprofile_updated_profile', array( $this, 'check_on_update' ), 0 );
	// ON AVATAR UPLOAD
	//on avatar delete

	// record on new avatar upload & check for profile completion
	add_action( 'xprofile_avatar_uploaded', array( $this, 'log_uploaded' ) );
	// on avatar delete, remove the log and mark profile incomplete.
	add_action( 'bp_core_delete_existing_avatar', array( $this, 'log_deleted' ) );
	// Show teh notice.
	add_action( 'bp_template_redirect', array( $this, 'check_profile_completion_state' ) );
}

/**
 * Check profile completion on login.
 *
 * @param string $user_login username.
 */
public function on_login_check( $user_login ) {

	$user = get_user_by( 'login', $user_login );
	if ( ! $user ) {
		return;
	}
	$this->check_on_update( $user->ID );
}

/**
 * Checks for profile update and triggers profile completed action.
 *
 * @param int $user_id user id.
 */
public function check_on_update( $user_id ) {

	$this->show_notice_and_redirect( $user_id );
}

/**
 * On New Avatar Upload, add the user meta to reflect that user has uploaded an avatar
 *
 * @param int $user_id user whose avatar changed.
 */
public function log_uploaded( $user_id ) {
	bp_update_user_meta( $user_id, 'has_avatar', 1 );
	$this->check_on_update( $user_id );
}

/**
 * On Delete Avatar, delete the user meta to reflect the change
 *
 * @param array $args see args array.
 */
public function log_deleted( $args ) {

	if ( $args['object'] != 'user' ) {
		return;
	}

	$user_id = empty( $args['item_id'] ) ? 0 : absint( $args['item_id'] );

	if ( ! $user_id ) {
		if ( bp_is_user() && ( bp_is_my_profile() || is_super_admin() ) ) {
			$user_id = bp_displayed_user_id();
		} else {
			$user_id = bp_loggedin_user_id();
		}
	}

	// we are sure it was user avatar delete
	// remove the log from user meta.
	bp_delete_user_meta( $user_id, 'has_avatar' );
	$this->mark_incomplete_profile( $user_id );
}

/**
 * Set custom member type if the profile just completed.
 *
 * @param int $user_id user id.
 */
public function set_custom_member_type( $user_id ) {
	bp_set_member_type( $user_id, $this->member_type, $this->append_member_type );
}

/**
 * Check's user's profile completion state.
 */
public function check_profile_completion_state() {
	if ( ! is_user_logged_in() ) {
		return;
	}

	$this->show_notice_and_redirect( bp_loggedin_user_id() );
}

/**
 * Check for profile completion and add notice.
 *
 * @param int $user_id user id.
 */
private function show_notice_and_redirect( $user_id ) {

	if ( ! $this->has_incomplete_profile( $user_id ) ) {
		// the profile is complete.
		return;
	}

	if ( is_super_admin( $user_id ) ) {
		return;// no need to force super admin.
	}
	if ( apply_filters( 'bp_force_profile_completion_skip_check', false ) ) {
	    return ;
    }

	$incomplete = true;
	// consider that we have the has_field and avatar available by default.
	$has_fields = $has_photo = true;

	// check if fields are required
	if ( $this->require_fields ) {
		$has_fields = $this->has_required_field_data( $user_id );
	}
	

	if ( $this->require_photo ) {
		$has_photo = $this->has_uploaded_avatar( $user_id );
	}

	$redirect_url = bp_core_get_user_domain( $user_id ) . bp_get_profile_slug();

	// this might have happened magically(most probably someone update profile by code).
	if ( $has_photo && $has_fields ) {
		$this->mark_complete_profile( $user_id );
		$incomplete = false;
		do_action( 'buddydev_bp_user_profile_completed', $user_id );
		do_action( 'arm_apply_plan_to_member', 2, $user_id); // 10/01/18: HD AJOUT pour rejoindre plan de base (& role: membre0)
	} elseif ( ! $has_fields ) {
		$this->notice = $this->notices['fields'];
		$redirect_url = $redirect_url . '/edit/';
	} else {
		$this->notice = $this->notices['photo'];
		$redirect_url = $redirect_url . '/change-avatar/';
	}

	if ( $incomplete ) {
		if ( $this->show_notice && $this->notice ) {
			bp_core_add_message( $this->notice, 'error' );
		}

		if ( $this->restrict_on_incomplete && ! bp_is_user_profile() ) {
			bp_core_redirect( $redirect_url );
		}
	}

}

/**
 * Mark profile incomplete.
 *
 * @param int $user_id user id.
 *
 * @return bool
 */
public function has_incomplete_profile( $user_id ) {

	if ( get_user_meta( $user_id, '_has_complete_profile', true ) ) {
		return false;
	}

	return true;
}

/**
 * Mark profile as incomplete.
 *
 * @param int $user_id user id.
 *
 * @return bool
 */
public function mark_incomplete_profile( $user_id ) {
	do_action( 'arm_apply_plan_to_member', 1, $user_id); // 10/01/18: HD AJOUT pour rejoindre plan sans profil finalisé (& role: subscriber)
	return delete_user_meta( $user_id, '_has_complete_profile' );
}

/**
 * Check if user has incomplete profile field data.
 *
 * @param int $user_id user id.
 *
 * @return bool|int
 */
public function mark_complete_profile( $user_id ) {
	return update_user_meta( $user_id, '_has_complete_profile', 1 );
}

public function has_uploaded_avatar( $user_id ) {
	// assuming that you are using force profile photo plugin that allows tracking of the user avatar.
	$has_avatar = bp_get_user_meta( $user_id, 'has_avatar', true );

	if ( ! $has_avatar ) {
		$has_avatar = bp_get_user_has_avatar( $user_id );// fallback.
	}

	return $has_avatar;
}

/**
 * Check if all required fields is complete.
 *
 * @param $user_id
 *
 * @return bool|mixed
 */
public function has_required_field_data( $user_id ) {
	global $wpdb;

	$has_fields_complete = get_user_meta( $user_id, '_has_required_field_data', true );
	if ( $has_fields_complete ) {
		return $has_fields_complete; // no need to test further.
	}

	$required_fields = $this->get_all_required_profile_fields();
	// no required field, so profile should be considered complete
	if ( empty( $required_fields ) ) {
		// update meta
		update_user_meta( $user_id, '_has_required_field_data', 1 );

		return true;
	}

	$fields_list = '(' . join( ',', $required_fields ) . ' )';

	$table = buddypress()->profile->table_name_data;

	$query = "SELECT field_id, value  FROM {$table} WHERE user_id = %d AND field_id IN {$fields_list}";

	$profile_entries = $wpdb->get_results( $wpdb->prepare( $query, $user_id ) );

	$complete = false;
	// not all fields are set.
	// shortcut.
	if ( count( $profile_entries ) != count( $required_fields ) ) {
		// unset flag.
		delete_user_meta( $user_id, '_has_required_field_data' );
	} else {
		$complete = true;
		// it should be complete but is the value acually not empty?
		// check by unserializing field values
		foreach ( $profile_entries as $profile_entry ) {
			$value = maybe_unserialize( $profile_entry->value );
			if ( empty( $value ) ) {
				$complete = false;
				break;
			}
		}
		if ( $complete ) {
			update_user_meta( $user_id, '_has_required_field_data', 1 );
		}
	}

	return $complete;
}

/**
 * Get all required field ids.
 *
 * @return array
 */
public function get_all_required_profile_fields() {

	global $wpdb;

	$table = buddypress()->profile->table_name_fields;

	$query = "SELECT id FROM {$table} WHERE is_required = %d";

	$fields = $wpdb->get_col( $wpdb->prepare( $query, 1 ) );

	return $fields;
}

}`

@HDInfautre
Copy link

HDInfautre commented Mar 19, 2018

`public function log_deleted( $args ) {

if ( $args['object'] != 'user' ) {
	return;
}

$user_id = empty( $args['item_id'] ) ? 0 : absint( $args['item_id'] );

if ( ! $user_id ) {
	if ( bp_is_user() && ( bp_is_my_profile() || is_super_admin() ) ) {
		$user_id = bp_displayed_user_id();
	} else {
		$user_id = bp_loggedin_user_id();
	}
}

$user = get_user_by( 'id', $user_id );
if( $user && ! is_super_admin( $user_id ) ) {
	$user->set_role('subscriber' );
}

}

new BuddyDev_Profile_Field_Completion_Helper();
class BuddyDev_Profile_Field_Completion_Helper {

/**
 * Which member type should be set? PLease change it.
 *
 * @var string
 */
private $member_type = 'membre0'; // Membre de base

/**
 * Ce type de membre doit-il être ajouté aux types de membre existants des utilisateurs ou remplacer? * Définissez true pour ajouter.
 *
 * @var bool
 */
private $append_member_type = false;

/**
 * Show notice to user?
 *
 * @var bool
 */
private $show_notice = true;

/**
 * Please do not set it. It ise used for temporary notice.
 *
 * @var string
 */
private $notice = '';


/**
 * Should we restrict user when they have incomplete profile?
 * It will force redirect user to complete their profile if enabled.
 *
 * @var bool
 */
private $restrict_on_incomplete = false; 

/**
 * require profile photo to decide the profile completion.
 *
 * @var bool
 */
private $require_photo = true;

/**
 * require profile field completion.
 *
 * @var bool
 */
private $require_fields = true;

/**
 * Notice messages based on required field or photo.
 *
 * @var array
 */
private $notices = array(
	'photo'  => 'Merci ajouter une photo de profil!!',
	'fields' => 'Merci de compléter votre profil.'
);

/**
 * Constructor.
 */
public function __construct() {
	add_filter( 'bp_settings_admin_nav', '__return_empty_array' );
	$this->setup();
}

/**
 * Setup hooks.
 */
public function setup() {
	// change member type when the profile completed(in the sense we want) is triggered.
	add_action( 'buddydev_bp_user_profile_completed', array( $this, 'set_custom_member_type' ) );

	// check on login for the profile completion.
	add_action( 'wp_login', array( $this, 'on_login_check' ), 0 );

	// check on account activation for the profile complete.
	add_action( 'bp_core_activated_user', array( $this, 'check_on_update' ) );
	// check on profile update for the profile completion.
	add_action( 'xprofile_updated_profile', array( $this, 'check_on_update' ), 0 );
	// ON AVATAR UPLOAD
	//on avatar delete

	// record on new avatar upload & check for profile completion
	add_action( 'xprofile_avatar_uploaded', array( $this, 'log_uploaded' ) );
	// on avatar delete, remove the log and mark profile incomplete.
	add_action( 'bp_core_delete_existing_avatar', array( $this, 'log_deleted' ) );
	// Show teh notice.
	add_action( 'bp_template_redirect', array( $this, 'check_profile_completion_state' ) );
}

/**
 * Check profile completion on login.
 *
 * @param string $user_login username.
 */
public function on_login_check( $user_login ) {

	$user = get_user_by( 'login', $user_login );
	if ( ! $user ) {
		return;
	}
	$this->check_on_update( $user->ID );
}

/**
 * Checks for profile update and triggers profile completed action.
 *
 * @param int $user_id user id.
 */
public function check_on_update( $user_id ) {

	$this->show_notice_and_redirect( $user_id );
}

/**
 * On New Avatar Upload, add the user meta to reflect that user has uploaded an avatar
 *
 * @param int $user_id user whose avatar changed.
 */
public function log_uploaded( $user_id ) {
	bp_update_user_meta( $user_id, 'has_avatar', 1 );
	$this->check_on_update( $user_id );
}

/**
 * On Delete Avatar, delete the user meta to reflect the change
 *
 * @param array $args see args array.
 */
public function log_deleted( $args ) {

	if ( $args['object'] != 'user' ) {
		return;
	}

	$user_id = empty( $args['item_id'] ) ? 0 : absint( $args['item_id'] );

	if ( ! $user_id ) {
		if ( bp_is_user() && ( bp_is_my_profile() || is_super_admin() ) ) {
			$user_id = bp_displayed_user_id();
		} else {
			$user_id = bp_loggedin_user_id();
		}
	}

	// we are sure it was user avatar delete
	// remove the log from user meta.
	bp_delete_user_meta( $user_id, 'has_avatar' );
	$this->mark_incomplete_profile( $user_id );
}

/**
 * Set custom member type if the profile just completed.
 *
 * @param int $user_id user id.
 */
public function set_custom_member_type( $user_id ) {
	bp_set_member_type( $user_id, $this->member_type, $this->append_member_type );
}

/**
 * Check's user's profile completion state.
 */
public function check_profile_completion_state() {
	if ( ! is_user_logged_in() ) {
		return;
	}

	$this->show_notice_and_redirect( bp_loggedin_user_id() );
}

/**
 * Check for profile completion and add notice.
 *
 * @param int $user_id user id.
 */
private function show_notice_and_redirect( $user_id ) {

	if ( ! $this->has_incomplete_profile( $user_id ) ) {
		// the profile is complete.
		return;
	}

	if ( is_super_admin( $user_id ) ) {
		return;// no need to force super admin.
	}
	if ( apply_filters( 'bp_force_profile_completion_skip_check', false ) ) {
	    return ;
    }

	$incomplete = true;
	// consider that we have the has_field and avatar available by default.
	$has_fields = $has_photo = true;

	// check if fields are required
	if ( $this->require_fields ) {
		$has_fields = $this->has_required_field_data( $user_id );
	}
	

	if ( $this->require_photo ) {
		$has_photo = $this->has_uploaded_avatar( $user_id );
	}

	$redirect_url = bp_core_get_user_domain( $user_id ) . bp_get_profile_slug();

	// this might have happened magically(most probably someone update profile by code).
	if ( $has_photo && $has_fields ) {
		$this->mark_complete_profile( $user_id );
		$incomplete = false;
		do_action( 'buddydev_bp_user_profile_completed', $user_id );
		do_action( 'arm_apply_plan_to_member', 2, $user_id); // 10/01/18: HD AJOUT pour rejoindre plan de base (& role: membre0)
	} elseif ( ! $has_fields ) {
		$this->notice = $this->notices['fields'];
		$redirect_url = $redirect_url . '/edit/';
	} else {
		$this->notice = $this->notices['photo'];
		$redirect_url = $redirect_url . '/change-avatar/';
	}

	if ( $incomplete ) {
		if ( $this->show_notice && $this->notice ) {
			bp_core_add_message( $this->notice, 'error' );
		}

		if ( $this->restrict_on_incomplete && ! bp_is_user_profile() ) {
			bp_core_redirect( $redirect_url );
		}
	}

}

/**
 * Mark profile incomplete.
 *
 * @param int $user_id user id.
 *
 * @return bool
 */
public function has_incomplete_profile( $user_id ) {

	if ( get_user_meta( $user_id, '_has_complete_profile', true ) ) {
		return false;
	}

	return true;
}

/**
 * Mark profile as incomplete.
 *
 * @param int $user_id user id.
 *
 * @return bool
 */
public function mark_incomplete_profile( $user_id ) {
	do_action( 'arm_apply_plan_to_member', 1, $user_id); // 10/01/18: HD AJOUT pour rejoindre plan sans profil finalisé (& role: subscriber)
	return delete_user_meta( $user_id, '_has_complete_profile' );
}

/**
 * Check if user has incomplete profile field data.
 *
 * @param int $user_id user id.
 *
 * @return bool|int
 */
public function mark_complete_profile( $user_id ) {
	return update_user_meta( $user_id, '_has_complete_profile', 1 );
}

public function has_uploaded_avatar( $user_id ) {
	// assuming that you are using force profile photo plugin that allows tracking of the user avatar.
	$has_avatar = bp_get_user_meta( $user_id, 'has_avatar', true );

	if ( ! $has_avatar ) {
		$has_avatar = bp_get_user_has_avatar( $user_id );// fallback.
	}

	return $has_avatar;
}

/**
 * Check if all required fields is complete.
 *
 * @param $user_id
 *
 * @return bool|mixed
 */
public function has_required_field_data( $user_id ) {
	global $wpdb;

	$has_fields_complete = get_user_meta( $user_id, '_has_required_field_data', true );
	if ( $has_fields_complete ) {
		return $has_fields_complete; // no need to test further.
	}

	$required_fields = $this->get_all_required_profile_fields();
	// no required field, so profile should be considered complete
	if ( empty( $required_fields ) ) {
		// update meta
		update_user_meta( $user_id, '_has_required_field_data', 1 );

		return true;
	}

	$fields_list = '(' . join( ',', $required_fields ) . ' )';

	$table = buddypress()->profile->table_name_data;

	$query = "SELECT field_id, value  FROM {$table} WHERE user_id = %d AND field_id IN {$fields_list}";

	$profile_entries = $wpdb->get_results( $wpdb->prepare( $query, $user_id ) );

	$complete = false;
	// not all fields are set.
	// shortcut.
	if ( count( $profile_entries ) != count( $required_fields ) ) {
		// unset flag.
		delete_user_meta( $user_id, '_has_required_field_data' );
	} else {
		$complete = true;
		// it should be complete but is the value acually not empty?
		// check by unserializing field values
		foreach ( $profile_entries as $profile_entry ) {
			$value = maybe_unserialize( $profile_entry->value );
			if ( empty( $value ) ) {
				$complete = false;
				break;
			}
		}
		if ( $complete ) {
			update_user_meta( $user_id, '_has_required_field_data', 1 );
		}
	}

	return $complete;
}

/**
 * Get all required field ids.
 *
 * @return array
 */
public function get_all_required_profile_fields() {

	global $wpdb;

	$table = buddypress()->profile->table_name_fields;

	$query = "SELECT id FROM {$table} WHERE is_required = %d";

	$fields = $wpdb->get_col( $wpdb->prepare( $query, 1 ) );

	return $fields;
}

}`

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment