BP_Xprofile_Profile_Completion_Widget
Profile Completion widget for the logged-in user
Description
Source
File: bp-xprofile/classes/class-bp-xprofile-profile-completion-widget.php
class BP_Xprofile_Profile_Completion_Widget extends WP_Widget {
public $widget_id = false;
public $transient_key = 'bbprofilecompletion';
/**
* Constructor.
*/
function __construct() {
// Set up optional widget args.
$widget_ops = array(
'classname' => 'widget_bp_profile_completion_widget widget buddypress',
'description' => __( 'Show Logged in user Profile Completion Progress.', 'buddyboss' ),
);
// Set up the widget.
parent::__construct(
false,
__( '(BB) Profile Completion', 'buddyboss' ),
$widget_ops
);
// Delete Transient hooks.
$this->delete_transient_hooks();
}
/**
* Function add hook to delete transient on various wp-admin and profile settings change.
* IF transient not deleted then it will show outdated content.
*/
function delete_transient_hooks() {
// Delete loggedin user transient only..
add_action( 'xprofile_avatar_uploaded', array( $this, 'delete_pc_loggedin_transient' ) ); // When profile photo uploaded from profile in Frontend.
add_action( 'xprofile_cover_image_uploaded', array( $this, 'delete_pc_loggedin_transient' ) ); // When cover photo uploaded from profile in Frontend.
add_action( 'bp_core_delete_existing_avatar', array( $this, 'delete_pc_loggedin_transient' ) ); // When profile photo deleted from profile in Frontend.
add_action( 'xprofile_cover_image_deleted', array( $this, 'delete_pc_loggedin_transient' ) ); // When cover photo deleted from profile in Frontend.
// Delete Profile Completion Transient when Profile updated, New Field added/update, field deleted etc..
add_action( 'xprofile_updated_profile', array( $this, 'delete_pc_transient' ) ); // On Profile updated from frontend.
add_action( 'xprofile_fields_saved_field', array( $this, 'delete_pc_transient' ) ); // On field added/updated in wp-admin > Profile
add_action( 'xprofile_fields_deleted_field', array( $this, 'delete_pc_transient' ) ); // On field deleted in wp-admin > profile.
add_action( 'xprofile_groups_deleted_group', array( $this, 'delete_pc_transient' ) ); // On profile group deleted in wp-admin.
add_action( 'update_option_bp-disable-avatar-uploads', array( $this, 'delete_pc_transient' ) ); // When avatar photo setting updated in wp-admin > Settings > profile.
add_action( 'update_option_bp-disable-cover-image-uploads', array( $this, 'delete_pc_transient' ) ); // When cover photo setting updated in wp-admin > Settings > profile.
add_action( 'wp_ajax_xprofile_reorder_fields', array( $this, 'delete_pc_transient' ) ); // When fields inside fieldset are dragged and dropped in wp-admin > buddybpss > profile.
}
/**
* Displays the widget.
*/
function widget( $args, $instance ) {
// do not do anything if user isn't logged in OR IF user is viewing other members profile.
if ( ! is_user_logged_in() || ( bp_is_user() && ! bp_is_my_profile() ) ) {
return;
}
/* Widget VARS */
$profile_groups_selected = $instance['profile_groups_enabled'];
$this->widget_id = $args['widget_id'];
$profile_phototype_selected = ! empty( $instance['profile_photos_enabled'] ) ? $instance['profile_photos_enabled'] : array();
$profile_hide_widget_selected = ! empty( $instance['profile_hide_widget'] ) ? $instance['profile_hide_widget'] : array();
$user_progress = $this->get_progress_data( $profile_groups_selected, $profile_phototype_selected );
// IF nothing selected then return and nothing to display.
if ( empty( $profile_groups_selected ) && empty( $profile_phototype_selected ) ) {
return;
}
// Hide the widget if "Hide widget once progress hits 100%" selected and progress is 100%
if ( 100 === (int) $user_progress['completion_percentage'] && ! empty( $instance['profile_hide_widget'] ) ) {
return;
}
/* Widget Template */
echo $args['before_widget'];
// Widget Title
echo $args['before_title'];
echo $instance['title'];
echo $args['after_title'];
// Widget Content
// Globalize the Profile Completion widget arguments. Used in the template called below.
$bp_nouveau = bp_nouveau();
$bp_nouveau->xprofile->profile_completion_widget_para = $user_progress;
bp_get_template_part( 'members/single/profile/widget' );
$bp_nouveau->xprofile->profile_completion_widget_para = array();
/**
* Fires after showing widget content.
*
* @since BuddyBoss 1.2.5
*/
do_action( 'xprofile_profile_completion_widget' );
echo $args['after_widget'];
}
/**
* Function returns user progress data by checking if data already exists in transient first. IF NO then follow checking the progress logic.
*
* Clear transient when 1) Widget form settings update. 2) When Logged user profile updated. 3) When new profile fields added/updated/deleted.
*
* @param type $profile_groups
* @param type $profile_phototype
*
* @return type
*/
function get_progress_data( $profile_groups, $profile_phototype ) {
$user_progress_formmatted = array();
// Check if data avail in transient.
$pc_transient_name = $this->get_pc_transient_name();
$pc_transient_data = get_transient( $pc_transient_name );
if ( ! empty( $pc_transient_data ) ) {
$user_progress_formmatted = $pc_transient_data;
} else {
// Get logged in user Progress.
$user_progress_arr = $this->get_user_progress( $profile_groups, $profile_phototype );
// Format User Progress array to pass on to the template.
$user_progress_formmatted = $this->get_user_progress_formatted( $user_progress_arr );
// set Transient here with 3hours expiration.
set_transient( $pc_transient_name, $user_progress_formmatted, HOUR_IN_SECONDS * 3 );
}
return $user_progress_formmatted;
}
/**
* Function trigger when profile updated. Profile field added/updated/deleted.
* Deletes Profile Completion Transient here.
*/
function delete_pc_loggedin_transient() {
// Delete logged in user all widgets transients from options table.
$user_id = get_current_user_id();
$transient_name_prefix = '%_transient_' . $this->transient_key . $user_id . '%';
$this->delete_transient_query( $transient_name_prefix );
}
/**
* Function trigger when profile updated. Profile field added/updated/deleted.
* Deletes Profile Completion Transient here.
*/
function delete_pc_transient() {
// Delete all users all widhets transients from options table.
$transient_name_prefix = '%_transient_' . $this->transient_key . '%';
$this->delete_transient_query( $transient_name_prefix );
}
/**
* Function deletes Transient based on the transient name specified.
*
* @param type $transient_name_prefix
*
* @global type $wpdb
*/
function delete_transient_query( $transient_name_prefix ) {
global $wpdb;
$sql = $wpdb->prepare(
"SELECT `option_name` FROM {$wpdb->options} WHERE option_name LIKE '%s' ",
$transient_name_prefix
);
$keys = $wpdb->get_col( $sql );
if ( ! empty( $keys ) ) {
foreach ( $keys as $transient ) {
delete_transient( str_replace( '_transient_', '', $transient ) );
}
}
}
/**
* Return Transient name using logged in User ID.
*
* @return string
*/
function get_pc_transient_name() {
$user_id = get_current_user_id();
$pc_transient_name = $this->transient_key . $user_id . $this->widget_id;
return $pc_transient_name;
}
/**
* Function returns logged in user progress based on options selected in the widget form.
*
* @param type $group_ids
* @param type $photo_types
*
* @return int
*/
function get_user_progress( $group_ids, $photo_types ) {
/* User Progress specific VARS. */
$user_id = get_current_user_id();
$progress_details = array();
$grand_total_fields = 0;
$grand_completed_fields = 0;
/* Profile Photo */
// check if profile photo option still enabled.
$is_profile_photo_disabled = bp_disable_avatar_uploads();
if ( ! $is_profile_photo_disabled && in_array( 'profile_photo', $photo_types ) ) {
++ $grand_total_fields;
$is_profile_photo_uploaded = ( bp_get_user_has_avatar( $user_id ) ) ? 1 : 0;
if ( $is_profile_photo_uploaded ) {
++ $grand_completed_fields;
}
$progress_details['photo_type']['profile_photo'] = array(
'is_uploaded' => $is_profile_photo_uploaded,
'name' => __( 'Profile Photo', 'buddyboss' ),
);
}
/* Cover Photo */
// check if cover photo option still enabled.
$is_cover_photo_disabled = bp_disable_cover_image_uploads();
if ( ! $is_cover_photo_disabled && in_array( 'cover_photo', $photo_types ) ) {
++ $grand_total_fields;
$is_cover_photo_uploaded = ( bp_attachments_get_user_has_cover_image( $user_id ) ) ? 1 : 0;
if ( $is_cover_photo_uploaded ) {
++ $grand_completed_fields;
}
$progress_details['photo_type']['cover_photo'] = array(
'is_uploaded' => $is_cover_photo_uploaded,
'name' => __( 'Cover Photo', 'buddyboss' ),
);
}
/* Groups Fields */
// Get Groups and Group fields with Loggedin user data.
$profile_groups = bp_xprofile_get_groups(
array(
'fetch_fields' => true,
'fetch_field_data' => true,
'user_id' => $user_id,
)
);
foreach ( $profile_groups as $single_group_details ) {
if ( empty( $single_group_details->fields ) ) {
continue;
}
/* Single Group Specific VARS */
$group_id = $single_group_details->id;
$single_group_progress = array();
// Consider only selected Groups ids from the widget form settings, skip all others.
if ( ! in_array( $group_id, $group_ids ) ) {
continue;
}
// Check if Current Group is repeater if YES then get number of fields inside current group.
$is_group_repeater_str = bp_xprofile_get_meta( $group_id, 'group', 'is_repeater_enabled', true );
$is_group_repeater = ( 'on' === $is_group_repeater_str ) ? true : false;
/* Loop through all the fields and check if fields completed or not. */
$group_total_fields = 0;
$group_completed_fields = 0;
foreach ( $single_group_details->fields as $group_single_field ) {
// If current group is repeater then only consider first set of fields.
if ( $is_group_repeater ) {
// If field not a "clone number 1" then stop. That means proceed with the first set of fields and restrict others.
$field_id = $group_single_field->id;
$clone_number = bp_xprofile_get_meta( $field_id, 'field', '_clone_number', true );
if ( $clone_number > 1 ) {
continue;
}
}
$field_data_value = maybe_unserialize( $group_single_field->data->value );
if ( ! empty( $field_data_value ) ) {
++ $group_completed_fields;
}
++ $group_total_fields;
}
/* Prepare array to return group specific progress details */
$single_group_progress['group_name'] = $single_group_details->name;
$single_group_progress['group_total_fields'] = $group_total_fields;
$single_group_progress['group_completed_fields'] = $group_completed_fields;
$grand_total_fields += $group_total_fields;
$grand_completed_fields += $group_completed_fields;
$progress_details['groups'][ $group_id ] = $single_group_progress;
}
/* Total Fields vs completed fields to calculate progress percentage. */
$progress_details['total_fields'] = $grand_total_fields;
$progress_details['completed_fields'] = $grand_completed_fields;
/**
* Filter returns User Progress array.
*
* @since BuddyBoss 1.2.5
*/
return apply_filters( 'xprofile_pc_user_progress', $progress_details );
}
/**
* Function formats user progress to pass on to templates.
*
* @param type $user_progress_arr
*
* @return int
*/
function get_user_progress_formatted( $user_progress_arr ) {
/* Groups */
$loggedin_user_domain = bp_loggedin_user_domain();
$profile_slug = bp_get_profile_slug();
// Calculate Total Progress percentage.
$profile_completion_percentage = round( ( $user_progress_arr['completed_fields'] * 100 ) / $user_progress_arr['total_fields'] );
$user_prgress_formatted = array(
'completion_percentage' => $profile_completion_percentage,
);
// Group specific details
$listing_number = 1;
foreach ( $user_progress_arr['groups'] as $group_id => $group_details ) {
$group_link = trailingslashit( $loggedin_user_domain . $profile_slug . '/edit/group/' . $group_id );
$user_prgress_formatted['groups'][] = array(
'number' => $listing_number,
'label' => $group_details['group_name'],
'link' => $group_link,
'is_group_completed' => ( $group_details['group_total_fields'] === $group_details['group_completed_fields'] ) ? true : false,
'total' => $group_details['group_total_fields'],
'completed' => $group_details['group_completed_fields'],
);
$listing_number ++;
}
/* Profile Photo */
if ( isset( $user_progress_arr['photo_type']['profile_photo'] ) ) {
$change_avatar_link = trailingslashit( $loggedin_user_domain . $profile_slug . '/change-avatar' );
$is_profile_uploaded = ( 1 === $user_progress_arr['photo_type']['profile_photo']['is_uploaded'] );
$user_prgress_formatted['groups'][] = array(
'number' => $listing_number,
'label' => $user_progress_arr['photo_type']['profile_photo']['name'],
'link' => $change_avatar_link,
'is_group_completed' => ( $is_profile_uploaded ) ? true : false,
'total' => 1,
'completed' => ( $is_profile_uploaded ) ? 1 : 0,
);
$listing_number ++;
}
/* Cover Photo */
if ( isset( $user_progress_arr['photo_type']['cover_photo'] ) ) {
$change_cover_link = trailingslashit( $loggedin_user_domain . $profile_slug . '/change-cover-image' );
$is_cover_uploaded = ( 1 === $user_progress_arr['photo_type']['cover_photo']['is_uploaded'] );
$user_prgress_formatted['groups'][] = array(
'number' => $listing_number,
'label' => $user_progress_arr['photo_type']['cover_photo']['name'],
'link' => $change_cover_link,
'is_group_completed' => ( $is_cover_uploaded ) ? true : false,
'total' => 1,
'completed' => ( $is_cover_uploaded ) ? 1 : 0,
);
$listing_number ++;
}
/**
* Filter returns User Progress array in the template friendly format.
*
* @since BuddyBoss 1.2.5
*/
return apply_filters( 'xprofile_pc_user_progress_formatted', $user_prgress_formatted );
}
/**
* Callback to save widget settings.
*/
function update( $new_instance, $old_instance ) {
$instance = $old_instance;
$instance['title'] = wp_strip_all_tags( $new_instance['title'] );
$instance['profile_groups_enabled'] = $new_instance['profile_groups_enabled'];
$instance['profile_photos_enabled'] = $new_instance['profile_photos_enabled'];
$instance['profile_hide_widget'] = $new_instance['profile_hide_widget'];
// Delete Transient.
$this->delete_pc_transient();
/**
* Fires when updating widget form settings.
*
* @since BuddyBoss 1.2.5
*/
return apply_filters( 'xprofile_profile_completion_form_update', $instance );
}
/**
* Widget settings form.
*/
function form( $instance ) {
$instance = wp_parse_args(
(array) $instance,
array(
'title' => __( 'Complete Your Profile', 'buddyboss' ),
)
);
/* Profile Groups and Profile Cover Photo VARS. */
$profile_groups = bp_xprofile_get_groups();
$photos_enabled_arr = array();
$widget_enabled_arr = array();
$is_profile_photo_disabled = bp_disable_avatar_uploads();
$is_cover_photo_disabled = bp_disable_cover_image_uploads();
// Show Options only when Profile Photo and Cover option enabled in the Profile Settings.
if ( ! $is_profile_photo_disabled ) {
$photos_enabled_arr['profile_photo'] = __( 'Profile Photo', 'buddyboss' );
}
if ( ! $is_cover_photo_disabled ) {
$photos_enabled_arr['cover_photo'] = __( 'Cover Photo', 'buddyboss' );
}
$widget_enabled_arr['hide_widget'] = __( 'Hide widget once progress hits 100%', 'buddyboss' );
/* Widget Form HTML */ ?>
<p>
<label for="<?php echo esc_attr( $this->get_field_id( 'title' ) ); ?>"><?php esc_html_e( 'Title:', 'buddyboss' ); ?></label>
<input class="widefat" id="<?php echo esc_attr( $this->get_field_id( 'title' ) ); ?>" name="<?php echo esc_attr( $this->get_field_name( 'title' ) ); ?>" type="text" value="<?php echo esc_attr( $instance['title'] ); ?>"/>
</p> <p>
<label><?php esc_html_e( 'Profile field sets:', 'buddyboss' ); ?></label>
<ul>
<?php
foreach ( $profile_groups as $single_group_details ) :
$is_checked = ( ! empty( $instance['profile_groups_enabled'] ) && in_array( $single_group_details->id, $instance['profile_groups_enabled'] ) );
?>
<li>
<label>
<input class="widefat" type="checkbox" name="<?php echo esc_attr( $this->get_field_name( 'profile_groups_enabled' ) ); ?>[]" value="<?php echo esc_attr( $single_group_details->id ); ?>"
<?php checked( $is_checked ); ?>
/>
<?php echo esc_html( $single_group_details->name ); ?>
</label>
</li>
<?php endforeach; ?>
</ul>
</p>
<?php if ( ! empty( $photos_enabled_arr ) ) : ?>
<p>
<label><?php esc_html_e( 'Profile photos:', 'buddyboss' ); ?></label>
<ul>
<?php foreach ( $photos_enabled_arr as $photos_value => $photos_label ) : ?>
<li>
<label>
<input class="widefat" type="checkbox" name="<?php echo esc_attr( $this->get_field_name( 'profile_photos_enabled' ) ); ?>[]" value="<?php echo esc_attr( $photos_value ); ?>" <?php checked( ( ! empty( $instance['profile_photos_enabled'] ) && in_array( $photos_value, $instance['profile_photos_enabled'] ) ) ); ?>/>
<?php echo esc_html( $photos_label ); ?>
</label>
</li>
<?php endforeach; ?>
</ul>
</p>
<?php endif; ?>
<?php if ( ! empty( $widget_enabled_arr ) ) : ?>
<p>
<label><?php esc_html_e( 'Options:', 'buddyboss' ); ?></label>
<ul>
<?php foreach ( $widget_enabled_arr as $option_value => $option_label ) : ?>
<li>
<label>
<input class="widefat" type="checkbox" name="<?php echo esc_attr( $this->get_field_name( 'profile_hide_widget' ) ); ?>[]" value="<?php echo esc_attr( $option_value ); ?>" <?php checked( ( ! empty( $instance['profile_hide_widget'] ) && in_array( $option_value, $instance['profile_hide_widget'] ) ) ); ?>/>
<?php echo esc_html( $option_label ); ?>
</label>
</li>
<?php endforeach; ?>
</ul>
</p>
<?php endif; ?>
<?php
/**
* Fires after showing last field in the Widget form.
*
* @since BuddyBoss 1.2.5
*/
do_action( 'xprofile_profile_completion_form' );
?>
<p>
<small><?php esc_html_e( 'Note: This widget is only displayed if a member is logged in.', 'buddyboss' ); ?></small>
</p>
<?php
}
}
Methods
- __construct — Constructor.
- delete_pc_loggedin_transient — Function trigger when profile updated. Profile field added/updated/deleted.
- delete_pc_transient — Function trigger when profile updated. Profile field added/updated/deleted.
- delete_transient_hooks — Function add hook to delete transient on various wp-admin and profile settings change.
- delete_transient_query — Function deletes Transient based on the transient name specified.
- form — Widget settings form.
- get_pc_transient_name — Return Transient name using logged in User ID.
- get_progress_data — Function returns user progress data by checking if data already exists in transient first. IF NO then follow checking the progress logic.
- get_user_progress — Function returns logged in user progress based on options selected in the widget form.
- get_user_progress_formatted — Function formats user progress to pass on to templates.
- update — Callback to save widget settings.
- widget — Displays the widget.
Questions?
We're always happy to help with code or other questions you might have! Search our developer docs, contact support, or connect with our sales team.