five-for-the-future/plugins/wporg-5ftf/includes/xprofile.php
Ian Dunn 428cde283c
Contributors: Clear object cache when resetting profile data.
BuddyPress sets it infinitely, so for some users it would be awhile before it would be pushed out organically.
2022-05-03 12:36:16 -07:00

204 lines
5.1 KiB
PHP

<?php
namespace WordPressDotOrg\FiveForTheFuture\XProfile;
use WordPressDotOrg\FiveForTheFuture\Contributor;
use wpdb;
/*
* The IDs of the xprofile fields we need. Better to use the numerical IDs than the field labels,
* because those are more likely to change.
*/
const FIELD_IDS = array(
'sponsored' => 24,
'hours_per_week' => 29,
'team_names' => 30,
);
defined( 'WPINC' ) || die();
/**
* Pull relevant data from profiles.wordpress.org.
*
* Note that this does not unserialize anything, it just pulls the raw values from the database table. If you
* want unserialized data, use `prepare_xprofile_contribution_data()`.
*
* @global wpdb $wpdb
*
* @param array $user_ids
*
* @return array
*/
function get_xprofile_contribution_data( array $user_ids ) {
global $wpdb;
$sql = $wpdb->prepare( '
SELECT user_id, field_id, value
FROM bpmain_bp_xprofile_data
WHERE user_id IN ( %1$s )
AND field_id IN ( %2$s )',
implode( ', ', array_map( 'absint', $user_ids ) ),
implode( ', ', array_map( 'absint', array_values( FIELD_IDS ) ) )
);
return $wpdb->get_results( $sql, ARRAY_A ); // phpcs:ignore WordPress.DB.PreparedSQL -- prepare called above.
}
/**
* Reindex the values by user ID, normalize it, and format it.
*
* This makes the data much easier to work with in many cases.
*
* @param array $raw_data
*
* @return array
*/
function prepare_xprofile_contribution_data( array $raw_data ) {
$prepared_data = array();
$field_keys_by_id = array_flip( FIELD_IDS );
foreach ( $raw_data as $datum ) {
$user_id = $datum['user_id'];
$field_key = $field_keys_by_id[ (int) $datum['field_id'] ];
$field_value = maybe_unserialize( $datum['value'] );
if ( ! isset( $prepared_data[ $user_id ]['sponsored'] ) ) {
$prepared_data[ $user_id ]['sponsored'] = false;
}
if ( 'sponsored' === $field_key ) {
$prepared_data[ $user_id ]['sponsored'] = 'Yes' === $field_value;
} else {
$prepared_data[ $user_id ][ $field_key ] = $field_value;
}
}
return $prepared_data;
}
/**
* Aggregate the raw xprofile data for all contributors linked to a given pledge.
*
* @param int $pledge_id
*
* @return array
*/
function get_aggregate_contributor_data_for_pledge( $pledge_id ) {
$contributor_posts = Contributor\get_pledge_contributors( $pledge_id, 'publish' );
// All of their contributors might have declined the invitation and had their posts deleted.
if ( ! $contributor_posts ) {
return array(
'contributors' => 0,
'hours' => 0,
'teams' => array(),
);
}
$contributor_users = Contributor\get_contributor_user_objects( $contributor_posts );
$user_ids = wp_list_pluck( $contributor_users, 'ID' );
$data = get_xprofile_contribution_data( $user_ids );
$initial = array(
'contributors' => count( $user_ids ),
'hours' => 0,
'teams' => array(),
);
$aggregate_data = array_reduce( $data, function( $carry, $item ) {
switch ( $item['field_id'] ) {
case FIELD_IDS['hours_per_week']:
$carry['hours'] += absint( $item['value'] );
break;
case FIELD_IDS['team_names']:
$value = (array) maybe_unserialize( $item['value'] );
$carry['teams'] = array_merge( $carry['teams'], $value );
break;
}
return $carry;
}, $initial );
$aggregate_data['teams'] = array_map(
function( $team ) {
// Fix for renamed team.
if ( 'Theme Review Team' === $team ) {
$team = 'Themes Team';
}
return $team;
},
$aggregate_data['teams']
);
$aggregate_data['teams'] = array_unique( $aggregate_data['teams'] );
sort( $aggregate_data['teams'] );
return $aggregate_data;
}
/**
* Fetch the profile data for a specific user.
*
* @param int $user_id
*
* @return array
*/
function get_contributor_user_data( $user_id ) {
$formatted_data = array();
$raw_data = get_xprofile_contribution_data( array( $user_id ) );
$defaults = array(
'hours_per_week' => 0,
'team_names' => array(),
);
foreach ( $raw_data as $datum ) {
$key = array_search( $datum['field_id'], FIELD_IDS );
switch ( $key ) {
case 'hours_per_week':
$formatted_data[ $key ] = absint( $datum['value'] );
break;
case 'team_names':
$formatted_data[ $key ] = maybe_unserialize( $datum['value'] );
}
}
$formatted_data = array_merge( $defaults, $formatted_data );
return $formatted_data;
}
/**
* Reset the 5ftF data on a user's profile.
*
* This deletes directly from the database and object cache -- rather than using something like
* `BP_XProfile_Field::delete()` -- because w.org/5 runs on a different network than profiles.w.org.
*/
function reset_contribution_data( $user_id ) : void {
global $wpdb;
$wpdb->query( $wpdb->prepare( '
DELETE FROM `bpmain_bp_xprofile_data`
WHERE
user_id = %d AND
field_id IN ( %d, %d, %d )',
$user_id,
FIELD_IDS['sponsored'],
FIELD_IDS['hours_per_week'],
FIELD_IDS['team_names'],
) );
wp_cache_add_global_groups( 'bp_xprofile_data' );
wp_cache_delete_multiple(
array(
$user_id . ':' . FIELD_IDS['sponsored'],
$user_id . ':' . FIELD_IDS['hours_per_week'],
$user_id . ':' . FIELD_IDS['team_names'],
),
'bp_xprofile_data'
);
}