/** * Pre-fetch member type data when initializing a Members loop. * * @since BuddyPress (2.2.0) * * @param BP_User_Query $bp_user_query BP_User_Query object. */ function bp_members_prefetch_member_type( BP_User_Query $bp_user_query ) { $uncached_member_ids = bp_get_non_cached_ids( $bp_user_query->user_ids, 'bp_member_type' ); $member_types = bp_get_object_terms( $uncached_member_ids, 'bp_member_type', array( 'fields' => 'all_with_object_id', ) ); // Rekey by user ID. $keyed_member_types = array(); foreach ( $member_types as $member_type ) { if ( ! isset( $keyed_member_types[ $member_type->object_id ] ) ) { $keyed_member_types[ $member_type->object_id ] = array(); } $keyed_member_types[ $member_type->object_id ][] = $member_type->name; } $cached_member_ids = array(); foreach ( $keyed_member_types as $user_id => $user_member_types ) { wp_cache_set( $user_id, $user_member_types, 'bp_member_type' ); $cached_member_ids[] = $user_id; } // Cache an empty value for users with no type. foreach ( array_diff( $uncached_member_ids, $cached_member_ids ) as $no_type_id ) { wp_cache_set( $no_type_id, '', 'bp_member_type' ); } }
/** * Get a user's profile data for a set of fields. * * @param int $user_id * @param array $field_ids * @return array */ public static function get_data_for_user($user_id, $field_ids) { global $wpdb; $data = array(); $cache_group = 'bp_xprofile_data_' . $user_id; $uncached_field_ids = bp_get_non_cached_ids($field_ids, $cache_group); // Prime the cache if (!empty($uncached_field_ids)) { $bp = buddypress(); $uncached_field_ids_sql = implode(',', wp_parse_id_list($uncached_field_ids)); $uncached_data = $wpdb->get_results($wpdb->prepare("SELECT id, user_id, field_id, value, last_updated FROM {$bp->profile->table_name_data} WHERE field_id IN ({$uncached_field_ids_sql}) AND user_id = %d", $user_id)); // Rekey $queried_data = array(); foreach ($uncached_data as $ud) { $d = new stdClass(); $d->id = $ud->id; $d->user_id = $ud->user_id; $d->field_id = $ud->field_id; $d->value = $ud->value; $d->last_updated = $ud->last_updated; $queried_data[$ud->field_id] = $d; } // Set caches foreach ($uncached_field_ids as $field_id) { // If a value was found, cache it if (isset($queried_data[$field_id])) { wp_cache_set($field_id, $queried_data[$field_id], $cache_group); // If no value was found, cache an empty item // to avoid future cache misses } else { $d = new stdClass(); $d->id = ''; $d->user_id = ''; $d->field_id = $field_id; $d->value = ''; $d->last_updated = ''; wp_cache_set($field_id, $d, $cache_group); } } } // Now that all items are cached, fetch them foreach ($field_ids as $field_id) { $data[] = wp_cache_get($field_id, $cache_group); } return $data; }
/** * Get last activity data for a user or set of users. * * @param int|array User IDs or multiple user IDs. * @return array */ public static function get_last_activity($user_id) { global $wpdb; // Sanitize and remove empty values $user_ids = array_filter(wp_parse_id_list($user_id)); if (empty($user_ids)) { return false; } $uncached_user_ids = bp_get_non_cached_ids($user_ids, 'bp_last_activity'); if (!empty($uncached_user_ids)) { $bp = buddypress(); $user_ids_sql = implode(',', $uncached_user_ids); $user_count = count($uncached_user_ids); $last_activities = $wpdb->get_results($wpdb->prepare("SELECT id, user_id, date_recorded FROM {$bp->members->table_name_last_activity} WHERE component = %s AND type = 'last_activity' AND user_id IN ({$user_ids_sql}) LIMIT {$user_count}", $bp->members->id)); foreach ($last_activities as $last_activity) { wp_cache_set($last_activity->user_id, array('user_id' => $last_activity->user_id, 'date_recorded' => $last_activity->date_recorded, 'activity_id' => $last_activity->id), 'bp_last_activity'); } } // Fetch all user data from the cache $retval = array(); foreach ($user_ids as $user_id) { $retval[$user_id] = wp_cache_get($user_id, 'bp_last_activity'); } return $retval; }
/** * Update the metadata cache for the specified objects. * * Based on WordPress's {@link update_meta_cache()}, this function primes the * cache with metadata related to a set of objects. This is typically done when * querying for a loop of objects; pre-fetching metadata for each queried * object can lead to dramatic performance improvements when using metadata * in the context of template loops. * * @since BuddyPress (1.6.0) * * @global object $wpdb WordPress database object for queries.. * * @param array $args { * Array of arguments. * @type array|string $object_ids List of object IDs to fetch metadata for. * Accepts an array or a comma-separated list of numeric IDs. * @type string $object_type The type of object, eg 'groups' or 'activity'. * @type string $meta_table The name of the metadata table being queried. * @type string $object_column Optional. The name of the database column where IDs * (those provided by $object_ids) are found. Eg, 'group_id' * for the groups metadata tables. Default: $object_type . '_id'. * @type string $cache_key_prefix Optional. The prefix to use when creating * cache key names. Default: the value of $meta_table. * } * * @return array|bool Metadata cache for the specified objects, or false on failure. */ function bp_update_meta_cache($args = array()) { global $wpdb; $defaults = array('object_ids' => array(), 'object_type' => '', 'cache_group' => '', 'meta_table' => '', 'object_column' => '', 'cache_key_prefix' => ''); $r = wp_parse_args($args, $defaults); extract($r); if (empty($object_ids) || empty($object_type) || empty($meta_table) || empty($cache_group)) { return false; } if (empty($cache_key_prefix)) { $cache_key_prefix = $meta_table; } if (empty($object_column)) { $object_column = $object_type . '_id'; } if (!$cache_group) { return false; } $object_ids = wp_parse_id_list($object_ids); $uncached_ids = bp_get_non_cached_ids($object_ids, $cache_group); $cache = array(); // Get meta info if (!empty($uncached_ids)) { $id_list = join(',', wp_parse_id_list($uncached_ids)); $meta_list = $wpdb->get_results(esc_sql("SELECT {$object_column}, meta_key, meta_value FROM {$meta_table} WHERE {$object_column} IN ({$id_list})"), ARRAY_A); if (!empty($meta_list)) { foreach ($meta_list as $metarow) { $mpid = intval($metarow[$object_column]); $mkey = $metarow['meta_key']; $mval = $metarow['meta_value']; // Force subkeys to be array type: if (!isset($cache[$mpid]) || !is_array($cache[$mpid])) { $cache[$mpid] = array(); } if (!isset($cache[$mpid][$mkey]) || !is_array($cache[$mpid][$mkey])) { $cache[$mpid][$mkey] = array(); } // Add a value to the current pid/key: $cache[$mpid][$mkey][] = $mval; } } foreach ($uncached_ids as $uncached_id) { // Cache empty values as well if (!isset($cache[$uncached_id])) { $cache[$uncached_id] = array(); } wp_cache_set($uncached_id, $cache[$uncached_id], $cache_group); } } return $cache; }
/** * Slurp up xprofilemeta for a specified set of profile objects. * * We do not use bp_update_meta_cache() for the xprofile component. This is * because the xprofile component has three separate object types (group, * field, and data) and three corresponding cache groups. Using the technique * in bp_update_meta_cache(), pre-fetching would take three separate database * queries. By grouping them together, we can reduce the required queries to * one. * * This function is called within a bp_has_profile() loop. * * @since BuddyPress (2.0.0) * * @param array $object_ids Multi-dimensional array of object_ids, keyed by * object type ('group', 'field', 'data') */ function bp_xprofile_update_meta_cache($object_ids = array()) { global $wpdb; // Bail if no objects if (empty($object_ids)) { return false; } $bp = buddypress(); // Define the array where uncached object IDs will be stored $uncached_object_ids = array('group', 'field', 'data'); // Define the cache groups for the 3 types of XProfile metadata $cache_groups = array('group' => 'xprofile_group_meta', 'field' => 'xprofile_field_meta', 'data' => 'xprofile_data_meta'); // No reason to query yet $do_query = false; // Loop through object types and look for uncached data foreach ($uncached_object_ids as $object_type) { // Skip if empty object type if (empty($object_ids[$object_type])) { continue; } // Sanitize $object_ids passed to the function $object_type_ids = wp_parse_id_list($object_ids[$object_type]); // Get non-cached IDs for each object type $uncached_object_ids[$object_type] = bp_get_non_cached_ids($object_type_ids, $cache_groups[$object_type]); // Set the flag to do the meta query if (!empty($uncached_object_ids[$object_type]) && false === $do_query) { $do_query = true; } } // Bail if no uncached items if (false === $do_query) { return; } // Setup where conditions for query $where_sql = ''; $where_conditions = array(); // Loop through uncached objects and prepare to query for them foreach ($uncached_object_ids as $otype => $oids) { // Skip empty object IDs if (empty($oids)) { continue; } // Compile WHERE query conditions for uncached metadata $oids_sql = implode(',', wp_parse_id_list($oids)); $where_conditions[] = $wpdb->prepare("( object_type = %s AND object_id IN ({$oids_sql}) )", $otype); } // Bail if no where conditions if (empty($where_conditions)) { return; } // Setup the WHERE query part $where_sql = implode(" OR ", $where_conditions); // Attempt to query meta values $meta_list = $wpdb->get_results("SELECT object_id, object_type, meta_key, meta_value FROM {$bp->profile->table_name_meta} WHERE {$where_sql}"); // Bail if no results found if (empty($meta_list) || is_wp_error($meta_list)) { return; } // Setup empty cache array $cache = array(); // Loop through metas foreach ($meta_list as $meta) { $oid = $meta->object_id; $otype = $meta->object_type; $okey = $meta->meta_key; $ovalue = $meta->meta_value; // Force subkeys to be array type if (!isset($cache[$otype][$oid]) || !is_array($cache[$otype][$oid])) { $cache[$otype][$oid] = array(); } if (!isset($cache[$otype][$oid][$okey]) || !is_array($cache[$otype][$oid][$okey])) { $cache[$otype][$oid][$okey] = array(); } // Add to the cache array $cache[$otype][$oid][$okey][] = maybe_unserialize($ovalue); } // Loop through data and cache to the appropriate object foreach ($cache as $object_type => $object_caches) { // Determine the cache group for this data $cache_group = $cache_groups[$object_type]; // Loop through objects and cache appropriately foreach ($object_caches as $object_id => $object_cache) { wp_cache_set($object_id, $object_cache, $cache_group); } } }
/** * Convert activity IDs to activity objects, as expected in template loop. * * @since 2.0.0 * * @param array $activity_ids Array of activity IDs. * @return array */ protected static function get_activity_data($activity_ids = array()) { global $wpdb; // Bail if no activity ID's passed. if (empty($activity_ids)) { return array(); } // Get BuddyPress. $bp = buddypress(); $activities = array(); $uncached_ids = bp_get_non_cached_ids($activity_ids, 'bp_activity'); // Prime caches as necessary. if (!empty($uncached_ids)) { // Format the activity ID's for use in the query below. $uncached_ids_sql = implode(',', wp_parse_id_list($uncached_ids)); // Fetch data from activity table, preserving order. $queried_adata = $wpdb->get_results("SELECT * FROM {$bp->activity->table_name} WHERE id IN ({$uncached_ids_sql})"); // Put that data into the placeholders created earlier, // and add it to the cache. foreach ((array) $queried_adata as $adata) { wp_cache_set($adata->id, $adata, 'bp_activity'); } } // Now fetch data from the cache. foreach ($activity_ids as $activity_id) { $activities[] = wp_cache_get($activity_id, 'bp_activity'); } // Then fetch user data. $user_query = new BP_User_Query(array('user_ids' => wp_list_pluck($activities, 'user_id'), 'populate_extras' => false)); // Associated located user data with activity items. foreach ($activities as $a_index => $a_item) { $a_user_id = intval($a_item->user_id); $a_user = isset($user_query->results[$a_user_id]) ? $user_query->results[$a_user_id] : ''; if (!empty($a_user)) { $activities[$a_index]->user_email = $a_user->user_email; $activities[$a_index]->user_nicename = $a_user->user_nicename; $activities[$a_index]->user_login = $a_user->user_login; $activities[$a_index]->display_name = $a_user->display_name; } } return $activities; }
/** * Populates the BP_XProfile_Group object with profile field groups, fields, * and field data * * @package BuddyPress XProfile * * @global object $wpdb WordPress DB access object. * * @param array $args { * Array of optional arguments: * @type int $profile_group_id Limit results to a single profile group. * @type int $user_id Required if you want to load a specific user's data. * Default: displayed user's ID. * @type array|string $member_type Limit fields by those restricted to a given member type, or array of * member types. If `$user_id` is provided, the value of `$member_type` * will be overridden by the member types of the provided user. The * special value of 'any' will return only those fields that are * unrestricted by member type - i.e., those applicable to any type. * @type bool $hide_empty_groups True to hide groups that don't have any fields. Default: false. * @type bool $hide_empty_fields True to hide fields where the user has not provided data. * Default: false. * @type bool $fetch_fields Whether to fetch each group's fields. Default: false. * @type bool $fetch_field_data Whether to fetch data for each field. Requires a $user_id. * Default: false. * @type array $exclude_groups Comma-separated list or array of group IDs to exclude. * @type array $exclude_fields Comma-separated list or array of field IDs to exclude. * @type bool $update_meta_cache Whether to pre-fetch xprofilemeta for all retrieved groups, fields, * and data. Default: true. * } * @return array $groups */ public static function get($args = array()) { global $wpdb; // Parse arguments. $r = wp_parse_args($args, array('profile_group_id' => false, 'user_id' => bp_displayed_user_id(), 'member_type' => false, 'hide_empty_groups' => false, 'hide_empty_fields' => false, 'fetch_fields' => false, 'fetch_field_data' => false, 'fetch_visibility_level' => false, 'exclude_groups' => false, 'exclude_fields' => false, 'update_meta_cache' => true)); // Keep track of object IDs for cache-priming. $object_ids = array('group' => array(), 'field' => array(), 'data' => array()); // WHERE. if (!empty($r['profile_group_id'])) { $where_sql = $wpdb->prepare('WHERE g.id = %d', $r['profile_group_id']); } elseif ($r['exclude_groups']) { $exclude = join(',', wp_parse_id_list($r['exclude_groups'])); $where_sql = "WHERE g.id NOT IN ({$exclude})"; } else { $where_sql = ''; } $bp = buddypress(); // Include or exclude empty groups. if (!empty($r['hide_empty_groups'])) { $group_ids = $wpdb->get_col("SELECT DISTINCT g.id FROM {$bp->profile->table_name_groups} g INNER JOIN {$bp->profile->table_name_fields} f ON g.id = f.group_id {$where_sql} ORDER BY g.group_order ASC"); } else { $group_ids = $wpdb->get_col("SELECT DISTINCT g.id FROM {$bp->profile->table_name_groups} g {$where_sql} ORDER BY g.group_order ASC"); } // Get all group data. $groups = self::get_group_data($group_ids); // Bail if not also getting fields. if (empty($r['fetch_fields'])) { return $groups; } // Get the group ids from the groups we found. $group_ids = wp_list_pluck($groups, 'id'); // Store for meta cache priming. $object_ids['group'] = $group_ids; // Bail if no groups found. if (empty($group_ids)) { return $groups; } // Setup IN query from group IDs. $group_ids_in = implode(',', (array) $group_ids); // Support arrays and comma-separated strings. $exclude_fields_cs = wp_parse_id_list($r['exclude_fields']); // Visibility - Handled here so as not to be overridden by sloppy use of the // exclude_fields parameter. See bp_xprofile_get_hidden_fields_for_user(). $hidden_user_fields = bp_xprofile_get_hidden_fields_for_user($r['user_id']); $exclude_fields_cs = array_merge($exclude_fields_cs, $hidden_user_fields); $exclude_fields_cs = implode(',', $exclude_fields_cs); // Set up NOT IN query for excluded field IDs. if (!empty($exclude_fields_cs)) { $exclude_fields_sql = "AND id NOT IN ({$exclude_fields_cs})"; } else { $exclude_fields_sql = ''; } // Set up IN query for included field IDs. $include_field_ids = array(); // Member-type restrictions. if (bp_get_member_types()) { if ($r['user_id'] || false !== $r['member_type']) { $member_types = $r['member_type']; if ($r['user_id']) { $member_types = bp_get_member_type($r['user_id'], false); if (empty($member_types)) { $member_types = array('null'); } } $member_types_fields = BP_XProfile_Field::get_fields_for_member_type($member_types); $include_field_ids += array_keys($member_types_fields); } } $in_sql = ''; if (!empty($include_field_ids)) { $include_field_ids_cs = implode(',', array_map('intval', $include_field_ids)); $in_sql = " AND id IN ({$include_field_ids_cs}) "; } // Fetch the fields. $field_ids = $wpdb->get_col("SELECT id FROM {$bp->profile->table_name_fields} WHERE group_id IN ( {$group_ids_in} ) AND parent_id = 0 {$exclude_fields_sql} {$in_sql} ORDER BY field_order"); // Bail if no fields. if (empty($field_ids)) { return $groups; } $field_ids = array_map('intval', $field_ids); // Prime the field cache. $uncached_field_ids = bp_get_non_cached_ids($field_ids, 'bp_xprofile_fields'); if (!empty($uncached_field_ids)) { $_uncached_field_ids = implode(',', array_map('intval', $uncached_field_ids)); $uncached_fields = $wpdb->get_results("SELECT * FROM {$bp->profile->table_name_fields} WHERE id IN ({$_uncached_field_ids})"); foreach ($uncached_fields as $uncached_field) { $fid = intval($uncached_field->id); wp_cache_set($fid, $uncached_field, 'bp_xprofile_fields'); } } // Pull field objects from the cache. $fields = array(); foreach ($field_ids as $field_id) { $fields[] = xprofile_get_field($field_id); } // Store field IDs for meta cache priming. $object_ids['field'] = $field_ids; // Maybe fetch field data. if (!empty($r['fetch_field_data'])) { // Get field data for user ID. if (!empty($field_ids) && !empty($r['user_id'])) { $field_data = BP_XProfile_ProfileData::get_data_for_user($r['user_id'], $field_ids); } // Remove data-less fields, if necessary. if (!empty($r['hide_empty_fields']) && !empty($field_ids) && !empty($field_data)) { // Loop through the results and find the fields that have data. foreach ((array) $field_data as $data) { // Empty fields may contain a serialized empty array. $maybe_value = maybe_unserialize($data->value); // Valid field values of 0 or '0' get caught by empty(), so we have an extra check for these. See #BP5731. if ((!empty($maybe_value) || '0' == $maybe_value) && false !== ($key = array_search($data->field_id, $field_ids))) { // Fields that have data get removed from the list. unset($field_ids[$key]); } } // The remaining members of $field_ids are empty. Remove them. foreach ($fields as $field_key => $field) { if (in_array($field->id, $field_ids)) { unset($fields[$field_key]); } } // Reset indexes. $fields = array_values($fields); } // Field data was found. if (!empty($fields) && !empty($field_data) && !is_wp_error($field_data)) { // Loop through fields. foreach ((array) $fields as $field_key => $field) { // Loop through the data in each field. foreach ((array) $field_data as $data) { // Assign correct data value to the field. if ($field->id == $data->field_id) { $fields[$field_key]->data = new stdClass(); $fields[$field_key]->data->value = $data->value; $fields[$field_key]->data->id = $data->id; } // Store for meta cache priming. $object_ids['data'][] = $data->id; } } } } // Prime the meta cache, if necessary. if (!empty($r['update_meta_cache'])) { bp_xprofile_update_meta_cache($object_ids); } // Maybe fetch visibility levels. if (!empty($r['fetch_visibility_level'])) { $fields = self::fetch_visibility_level($r['user_id'], $fields); } // Merge the field array back in with the group array. foreach ((array) $groups as $group) { // Indexes may have been shifted after previous deletions, so we get a // fresh one each time through the loop. $index = array_search($group, $groups); foreach ((array) $fields as $field) { if ($group->id === $field->group_id) { $groups[$index]->fields[] = $field; } } // When we unset fields above, we may have created empty groups. // Remove them, if necessary. if (empty($group->fields) && !empty($r['hide_empty_groups'])) { unset($groups[$index]); } // Reset indexes. $groups = array_values($groups); } return $groups; }
/** * Update the metadata cache for the specified objects. * * Based on WordPress's {@link update_meta_cache()}, this function primes the * cache with metadata related to a set of objects. This is typically done when * querying for a loop of objects; pre-fetching metadata for each queried * object can lead to dramatic performance improvements when using metadata * in the context of template loops. * * @since BuddyPress (1.6.0) * * @global $wpdb WordPress database object for queries.. * * @param array $args { * Array of arguments. * @type array|string $object_ids List of object IDs to fetch metadata for. * Accepts an array or a comma-separated list of numeric IDs. * @type string $object_type The type of object, eg 'groups' or 'activity'. * @type string $meta_table The name of the metadata table being queried. * @type string $object_column Optional. The name of the database column * where IDs (those provided by $object_ids) are found. Eg, 'group_id' * for the groups metadata tables. Default: $object_type . '_id'. * @type string $cache_key_prefix Optional. The prefix to use when creating * cache key names. Default: the value of $meta_table. * } * @return array|bool Metadata cache for the specified objects, or false on failure. */ function bp_update_meta_cache( $args = array() ) { global $wpdb; $defaults = array( 'object_ids' => array(), // Comma-separated list or array of item ids 'object_type' => '', // Canonical component id: groups, members, etc 'cache_group' => '', // Cache group 'meta_table' => '', // Name of the table containing the metadata 'object_column' => '', // DB column for the object ids (group_id, etc) 'cache_key_prefix' => '' // Prefix to use when creating cache key names. Eg // 'bp_groups_groupmeta' ); $r = wp_parse_args( $args, $defaults ); extract( $r ); if ( empty( $object_ids ) || empty( $object_type ) || empty( $meta_table ) || empty( $cache_group ) ) { return false; } if ( empty( $cache_key_prefix ) ) { $cache_key_prefix = $meta_table; } if ( empty( $object_column ) ) { $object_column = $object_type . '_id'; } if ( ! $cache_group ) { return false; } $object_ids = wp_parse_id_list( $object_ids ); $uncached_ids = bp_get_non_cached_ids( $object_ids, $cache_group ); $cache = array(); // Get meta info if ( ! empty( $uncached_ids ) ) { $id_list = join( ',', wp_parse_id_list( $uncached_ids ) ); $meta_list = $wpdb->get_results( esc_sql( "SELECT {$object_column}, meta_key, meta_value FROM {$meta_table} WHERE {$object_column} IN ({$id_list})" ), ARRAY_A ); if ( ! empty( $meta_list ) ) { foreach ( $meta_list as $metarow ) { $mpid = intval( $metarow[$object_column] ); $mkey = $metarow['meta_key']; $mval = $metarow['meta_value']; // Force subkeys to be array type: if ( !isset( $cache[$mpid] ) || !is_array( $cache[$mpid] ) ) $cache[$mpid] = array(); if ( !isset( $cache[$mpid][$mkey] ) || !is_array( $cache[$mpid][$mkey] ) ) $cache[$mpid][$mkey] = array(); // Add a value to the current pid/key: $cache[$mpid][$mkey][] = $mval; } } foreach ( $uncached_ids as $uncached_id ) { // Cache empty values as well if ( ! isset( $cache[ $uncached_id ] ) ) { $cache[ $uncached_id ] = array(); } wp_cache_set( $uncached_id, $cache[ $uncached_id ], $cache_group ); } } return $cache; }
/** * Get the friendships for a given user. * * @since 2.6.0 * * @param int $user_id ID of the user whose friends are being retrieved. * @param array $args { * Optional. Filter parameters. * @type int $id ID of specific friendship to retrieve. * @type int $initiator_user_id ID of friendship initiator. * @type int $friend_user_id ID of specific friendship to retrieve. * @type int $is_confirmed Whether the friendship has been accepted. * @type int $is_limited Whether the friendship is limited. * @type string $order_by Column name to order by. * @type string $sort_order ASC or DESC. Default DESC. * } * @param string $operator Optional. Operator to use in `wp_list_filter()`. * * @return array $friendships Array of friendship objects. */ public static function get_friendships($user_id, $args = array(), $operator = 'AND') { if (empty($user_id)) { $user_id = bp_loggedin_user_id(); } $r = bp_parse_args($args, array('id' => null, 'initiator_user_id' => null, 'friend_user_id' => null, 'is_confirmed' => null, 'is_limited' => null, 'order_by' => 'date_created', 'sort_order' => 'DESC', 'page' => null, 'per_page' => null), 'bp_get_user_friendships'); // First, we get all friendships that involve the user. $friendship_ids = wp_cache_get($user_id, 'bp_friends_friendships_for_user'); if (false === $friendship_ids) { $friendship_ids = self::get_friendship_ids_for_user($user_id); wp_cache_set($user_id, $friendship_ids, 'bp_friends_friendships_for_user'); } // Prime the membership cache. $uncached_friendship_ids = bp_get_non_cached_ids($friendship_ids, 'bp_friends_friendships'); if (!empty($uncached_friendship_ids)) { $uncached_friendships = self::get_friendships_by_id($uncached_friendship_ids); foreach ($uncached_friendships as $uncached_friendship) { wp_cache_set($uncached_friendship->id, $uncached_friendship, 'bp_friends_friendships'); } } // Assemble filter array. $filters = wp_array_slice_assoc($r, array('id', 'initiator_user_id', 'friend_user_id', 'is_confirmed', 'is_limited')); foreach ($filters as $filter_name => $filter_value) { if (is_null($filter_value)) { unset($filters[$filter_name]); } } // Populate friendship array from cache, and normalize. $friendships = array(); $int_keys = array('id', 'initiator_user_id', 'friend_user_id'); $bool_keys = array('is_confirmed', 'is_limited'); foreach ($friendship_ids as $friendship_id) { // Create a limited BP_Friends_Friendship object (don't fetch the user details). $friendship = new BP_Friends_Friendship($friendship_id, false, false); // Sanity check. if (!isset($friendship->id)) { continue; } // Integer values. foreach ($int_keys as $index) { $friendship->{$index} = intval($friendship->{$index}); } // Boolean values. foreach ($bool_keys as $index) { $friendship->{$index} = (bool) $friendship->{$index}; } // We need to support the same operators as wp_list_filter(). if ('OR' == $operator || 'NOT' == $operator) { $matched = 0; foreach ($filters as $filter_name => $filter_value) { if (isset($friendship->{$filter_name}) && $filter_value == $friendship->{$filter_name}) { $matched++; } } if ('OR' == $operator && $matched > 0 || 'NOT' == $operator && 0 == $matched) { $friendships[$friendship->id] = $friendship; } } else { /* * This is the more typical 'AND' style of filter. * If any of the filters miss, we move on. */ foreach ($filters as $filter_name => $filter_value) { if (!isset($friendship->{$filter_name}) || $filter_value != $friendship->{$filter_name}) { continue 2; } } $friendships[$friendship->id] = $friendship; } } // Sort the results on a column name. if (in_array($r['order_by'], array('id', 'initiator_user_id', 'friend_user_id'))) { $friendships = bp_sort_by_key($friendships, $r['order_by'], 'num', true); } // Adjust the sort direction of the results. if ('ASC' === strtoupper($r['sort_order'])) { // `true` to preserve keys. $friendships = array_reverse($friendships, true); } // Paginate the results. if ($r['per_page'] && $r['page']) { $start = ($r['page'] - 1) * $r['per_page']; $friendships = array_slice($friendships, $start, $r['per_page']); } return $friendships; }
/** * Get a list of groups of which the specified user is a member. * * Get a list of the groups to which this member belongs, * filtered by group membership status and role. * Usage examples: Used with no arguments specified, * * bp_get_user_groups( bp_loggedin_user_id() ); * * returns an array of the groups in which the logged-in user * is an unpromoted member. To fetch an array of all groups that * the current user belongs to, in any membership role, * member, moderator or administrator, use * * bp_get_user_groups( $user_id, array( * 'is_admin' => null, * 'is_mod' => null, * ) ); * * @since 2.6.0 * * @param int $user_id ID of the user. * @param array $args { * Array of optional args. * @param bool|null $is_confirmed Whether to return only confirmed memberships. Pass `null` to disable this * filter. Default: true. * @param bool|null $is_banned Whether to return only banned memberships. Pass `null` to disable this filter. * Default: false. * @param bool|null $is_admin Whether to return only admin memberships. Pass `null` to disable this filter. * Default: false. * @param bool|null $is_mod Whether to return only mod memberships. Pass `null` to disable this filter. * Default: false. * @param bool|null $invite_sent Whether to return only memberships with 'invite_sent'. Pass `null` to disable * this filter. Default: false. * @param string $orderby Field to order by. Accepts 'id' (membership ID), 'group_id', 'date_modified'. * Default: 'group_id'. * @param string $order Sort order. Accepts 'ASC' or 'DESC'. Default: 'ASC'. * } * @return array Array of matching group memberships, keyed by group ID. */ function bp_get_user_groups($user_id, $args = array()) { $r = bp_parse_args($args, array('is_confirmed' => true, 'is_banned' => false, 'is_admin' => false, 'is_mod' => false, 'invite_sent' => null, 'orderby' => 'group_id', 'order' => 'ASC'), 'get_user_groups'); $user_id = intval($user_id); $membership_ids = wp_cache_get($user_id, 'bp_groups_memberships_for_user'); if (false === $membership_ids) { $membership_ids = BP_Groups_Member::get_membership_ids_for_user($user_id); wp_cache_set($user_id, $membership_ids, 'bp_groups_memberships_for_user'); } // Prime the membership cache. $uncached_membership_ids = bp_get_non_cached_ids($membership_ids, 'bp_groups_memberships'); if (!empty($uncached_membership_ids)) { $uncached_memberships = BP_Groups_Member::get_memberships_by_id($uncached_membership_ids); foreach ($uncached_memberships as $uncached_membership) { wp_cache_set($uncached_membership->id, $uncached_membership, 'bp_groups_memberships'); } } // Assemble filter array for use in `wp_list_filter()`. $filters = wp_array_slice_assoc($r, array('is_confirmed', 'is_banned', 'is_admin', 'is_mod', 'invite_sent')); foreach ($filters as $filter_name => $filter_value) { if (is_null($filter_value)) { unset($filters[$filter_name]); } } // Populate group membership array from cache, and normalize. $groups = array(); $int_keys = array('id', 'group_id', 'user_id', 'inviter_id'); $bool_keys = array('is_admin', 'is_mod', 'is_confirmed', 'is_banned', 'invite_sent'); foreach ($membership_ids as $membership_id) { $membership = wp_cache_get($membership_id, 'bp_groups_memberships'); // Sanity check. if (!isset($membership->group_id)) { continue; } // Integer values. foreach ($int_keys as $index) { $membership->{$index} = intval($membership->{$index}); } // Boolean values. foreach ($bool_keys as $index) { $membership->{$index} = (bool) $membership->{$index}; } foreach ($filters as $filter_name => $filter_value) { if (!isset($membership->{$filter_name}) || $filter_value != $membership->{$filter_name}) { continue 2; } } $group_id = (int) $membership->group_id; $groups[$group_id] = $membership; } // By default, results are ordered by membership id. if ('group_id' === $r['orderby']) { ksort($groups); } elseif (in_array($r['orderby'], array('id', 'date_modified'))) { $groups = bp_sort_by_key($groups, $r['orderby']); } // By default, results are ordered ASC. if ('DESC' === strtoupper($r['order'])) { // `true` to preserve keys. $groups = array_reverse($groups, true); } return $groups; }
/** * Slurp up xprofilemeta for a specified set of profile objects. * * We do not use bp_update_meta_cache() for the xprofile component. This is * because the xprofile component has three separate object types (group, * field, and data) and three corresponding cache groups. Using the technique * in bp_update_meta_cache(), pre-fetching would take three separate database * queries. By grouping them together, we can reduce the required queries to * one. * * This function is called within a bp_has_profile() loop. * * @since BuddyPress (2.0.0) * * @param array $object_ids Multi-dimensional array of object_ids, keyed by * object type ('group', 'field', 'data') */ function bp_xprofile_update_meta_cache($object_ids = array(), $user_id = 0) { global $wpdb; if (empty($object_ids)) { return false; } // $object_ids is a multi-dimensional array $uncached_object_ids = array('group' => array(), 'field' => array(), 'data' => array()); $cache_groups = array('group' => 'xprofile_group_meta', 'field' => 'xprofile_field_meta', 'data' => 'xprofile_data_meta'); $do_query = false; foreach ($uncached_object_ids as $object_type => $uncached_object_type_ids) { if (!empty($object_ids[$object_type])) { // Sanitize $object_ids passed to the function $object_type_ids = wp_parse_id_list($object_ids[$object_type]); // Get non-cached IDs for each object type $uncached_object_ids[$object_type] = bp_get_non_cached_ids($object_type_ids, $cache_groups[$object_type]); // Set the flag to do the meta query if (!empty($uncached_object_ids[$object_type]) && !$do_query) { $do_query = true; } } } // If there are uncached items, go ahead with the query if ($do_query) { $where = array(); foreach ($uncached_object_ids as $otype => $oids) { if (empty($oids)) { continue; } $oids_sql = implode(',', wp_parse_id_list($oids)); $where[] = $wpdb->prepare("( object_type = %s AND object_id IN ({$oids_sql}) )", $otype); } $where_sql = implode(" OR ", $where); } $bp = buddypress(); $cache = array(); $meta_list = $wpdb->get_results("SELECT object_id, object_type, meta_key, meta_value FROM {$bp->profile->table_name_meta} WHERE {$where_sql}"); if (!empty($meta_list)) { foreach ($meta_list as $meta) { $oid = $meta->object_id; $otype = $meta->object_type; $okey = $meta->meta_key; $ovalue = $meta->meta_value; // Force subkeys to be array type if (!isset($cache[$otype][$oid]) || !is_array($cache[$otype][$oid])) { $cache[$otype][$oid] = array(); } if (!isset($cache[$otype][$oid][$okey]) || !is_array($cache[$otype][$oid][$okey])) { $cache[$otype][$oid][$okey] = array(); } // Add to the cache array $cache[$otype][$oid][$okey][] = maybe_unserialize($ovalue); } foreach ($cache as $object_type => $object_caches) { $cache_group = $cache_groups[$object_type]; foreach ($object_caches as $object_id => $object_cache) { wp_cache_set($object_id, $object_cache, $cache_group); } } } return; }
/** * Prime the bp_group_admins cache for one or more groups. * * @since 2.7.0 * * @param array $group_ids IDs of the groups. * @return bool True on success. */ public static function prime_group_admins_mods_cache($group_ids) { global $wpdb; $uncached = bp_get_non_cached_ids($group_ids, 'bp_group_admins'); if ($uncached) { $bp = buddypress(); $uncached_sql = implode(',', array_map('intval', $uncached)); $group_admin_mods = $wpdb->get_results("SELECT user_id, group_id, date_modified, is_admin, is_mod FROM {$bp->groups->table_name_members} WHERE group_id IN ({$uncached_sql}) AND ( is_admin = 1 OR is_mod = 1 ) AND is_banned = 0"); $admins = $mods = array(); if ($group_admin_mods) { foreach ($group_admin_mods as $group_admin_mod) { $obj = new stdClass(); $obj->user_id = $group_admin_mod->user_id; $obj->date_modified = $group_admin_mod->date_modified; if ($group_admin_mod->is_admin) { $admins[$group_admin_mod->group_id][] = $obj; } else { $mods[$group_admin_mod->group_id][] = $obj; } } } // Prime cache for all groups, even those with no matches. foreach ($uncached as $group_id) { $group_admins = isset($admins[$group_id]) ? $admins[$group_id] : array(); wp_cache_set($group_id, $group_admins, 'bp_group_admins'); $group_mods = isset($mods[$group_id]) ? $mods[$group_id] : array(); wp_cache_set($group_id, $group_mods, 'bp_group_mods'); } } }
/** * Query for groups. * * @see WP_Meta_Query::queries for a description of the 'meta_query' * parameter format. * * @since 1.6.0 * @since 2.6.0 Added `$group_type`, `$group_type__in`, and `$group_type__not_in` parameters. * @since 2.7.0 Added `$update_admin_cache` and `$parent_id` parameters. * * @param array $args { * Array of parameters. All items are optional. * @type string $type Optional. Shorthand for certain orderby/order combinations. * 'newest', 'active', 'popular', 'alphabetical', 'random'. * When present, will override orderby and order params. * Default: null. * @type string $orderby Optional. Property to sort by. 'date_created', 'last_activity', * 'total_member_count', 'name', 'random'. Default: 'date_created'. * @type string $order Optional. Sort order. 'ASC' or 'DESC'. Default: 'DESC'. * @type int $per_page Optional. Number of items to return per page of results. * Default: null (no limit). * @type int $page Optional. Page offset of results to return. * Default: null (no limit). * @type int $user_id Optional. If provided, results will be limited to groups * of which the specified user is a member. Default: null. * @type string $search_terms Optional. If provided, only groups whose names or descriptions * match the search terms will be returned. Default: false. * @type array|string $group_type Array or comma-separated list of group types to limit results to. * @type array|string $group_type__in Array or comma-separated list of group types to limit results to. * @type array|string $group_type__not_in Array or comma-separated list of group types that will be * excluded from results. * @type array $meta_query Optional. An array of meta_query conditions. * See {@link WP_Meta_Query::queries} for description. * @type array|string $value Optional. Array or comma-separated list of group IDs. Results * will be limited to groups within the list. Default: false. * @type array|string $parent_id Optional. Array or comma-separated list of group IDs. Results * will be limited to children of the specified groups. Default: null. * @type array|string $exclude Optional. Array or comma-separated list of group IDs. * Results will exclude the listed groups. Default: false. * @type bool $update_meta_cache Whether to pre-fetch groupmeta for the returned groups. * Default: true. * @type bool $update_admin_cache Whether to pre-fetch administrator IDs for the returned * groups. Default: false. * @type bool $show_hidden Whether to include hidden groups in results. Default: false. * } * @return array { * @type array $groups Array of group objects returned by the * paginated query. * @type int $total Total count of all groups matching non- * paginated query params. * } */ public static function get($args = array()) { global $wpdb; // Backward compatibility with old method of passing arguments. if (!is_array($args) || func_num_args() > 1) { _deprecated_argument(__METHOD__, '1.7', sprintf(__('Arguments passed to %1$s should be in an associative array. See the inline documentation at %2$s for more details.', 'buddypress'), __METHOD__, __FILE__)); $old_args_keys = array(0 => 'type', 1 => 'per_page', 2 => 'page', 3 => 'user_id', 4 => 'search_terms', 5 => 'include', 6 => 'populate_extras', 7 => 'exclude', 8 => 'show_hidden'); $func_args = func_get_args(); $args = bp_core_parse_args_array($old_args_keys, $func_args); } $defaults = array('type' => null, 'orderby' => 'date_created', 'order' => 'DESC', 'per_page' => null, 'page' => null, 'user_id' => 0, 'search_terms' => false, 'group_type' => '', 'group_type__in' => '', 'group_type__not_in' => '', 'meta_query' => false, 'include' => false, 'parent_id' => null, 'update_meta_cache' => true, 'update_admin_cache' => false, 'exclude' => false, 'show_hidden' => false); $r = wp_parse_args($args, $defaults); $bp = buddypress(); $sql = array('select' => "SELECT DISTINCT g.id", 'from' => "{$bp->groups->table_name} g", 'where' => '', 'orderby' => '', 'pagination' => ''); if (!empty($r['user_id'])) { $sql['from'] .= " JOIN {$bp->groups->table_name_members} m ON ( g.id = m.group_id )"; } $where_conditions = array(); if (empty($r['show_hidden'])) { $where_conditions['hidden'] = "g.status != 'hidden'"; } if (!empty($r['search_terms'])) { $search_terms_like = '%' . bp_esc_like($r['search_terms']) . '%'; $where_conditions['search'] = $wpdb->prepare("( g.name LIKE %s OR g.description LIKE %s )", $search_terms_like, $search_terms_like); } $meta_query_sql = self::get_meta_query_sql($r['meta_query']); if (!empty($meta_query_sql['join'])) { $sql['from'] .= $meta_query_sql['join']; } if (!empty($meta_query_sql['where'])) { $where_conditions['meta'] = $meta_query_sql['where']; } // Only use 'group_type__in', if 'group_type' is not set. if (empty($r['group_type']) && !empty($r['group_type__in'])) { $r['group_type'] = $r['group_type__in']; } // Group types to exclude. This has priority over inclusions. if (!empty($r['group_type__not_in'])) { $group_type_clause = self::get_sql_clause_for_group_types($r['group_type__not_in'], 'NOT IN'); // Group types to include. } elseif (!empty($r['group_type'])) { $group_type_clause = self::get_sql_clause_for_group_types($r['group_type'], 'IN'); } if (!empty($group_type_clause)) { $where_conditions['group_type'] = $group_type_clause; } if (!empty($r['user_id'])) { $where_conditions['user'] = $wpdb->prepare("m.user_id = %d AND m.is_confirmed = 1 AND m.is_banned = 0", $r['user_id']); } if (!empty($r['include'])) { $include = implode(',', wp_parse_id_list($r['include'])); $where_conditions['include'] = "g.id IN ({$include})"; } if (!is_null($r['parent_id'])) { // Note that `wp_parse_id_list()` converts `false` to 0. $parent_id = implode(',', wp_parse_id_list($r['parent_id'])); $where_conditions['parent_id'] = "g.parent_id IN ({$parent_id})"; } if (!empty($r['exclude'])) { $exclude = implode(',', wp_parse_id_list($r['exclude'])); $where_conditions['exclude'] = "g.id NOT IN ({$exclude})"; } /* Order/orderby ********************************************/ $order = $r['order']; $orderby = $r['orderby']; // If a 'type' parameter was passed, parse it and overwrite // 'order' and 'orderby' params passed to the function. if (!empty($r['type'])) { /** * Filters the 'type' parameter used to overwrite 'order' and 'orderby' values. * * @since 2.1.0 * * @param array $value Converted 'type' value for order and orderby. * @param string $value Parsed 'type' value for the get method. */ $order_orderby = apply_filters('bp_groups_get_orderby', self::convert_type_to_order_orderby($r['type']), $r['type']); // If an invalid type is passed, $order_orderby will be // an array with empty values. In this case, we stick // with the default values of $order and $orderby. if (!empty($order_orderby['order'])) { $order = $order_orderby['order']; } if (!empty($order_orderby['orderby'])) { $orderby = $order_orderby['orderby']; } } // 'total_member_count' and 'last_activity' sorts require additional table joins. if ('total_member_count' === $orderby) { $sql['from'] .= " JOIN {$bp->groups->table_name_groupmeta} gm_total_member_count ON ( g.id = gm_total_member_count.group_id )"; $where_conditions['total_member_count'] = "gm_total_member_count.meta_key = 'total_member_count'"; } elseif ('last_activity' === $orderby) { $sql['from'] .= " JOIN {$bp->groups->table_name_groupmeta} gm_last_activity on ( g.id = gm_last_activity.group_id )"; $where_conditions['last_activity'] = "gm_last_activity.meta_key = 'last_activity'"; } // Sanitize 'order'. $order = bp_esc_sql_order($order); /** * Filters the converted 'orderby' term. * * @since 2.1.0 * * @param string $value Converted 'orderby' term. * @param string $orderby Original orderby value. * @param string $value Parsed 'type' value for the get method. */ $orderby = apply_filters('bp_groups_get_orderby_converted_by_term', self::convert_orderby_to_order_by_term($orderby), $orderby, $r['type']); // Random order is a special case. if ('rand()' === $orderby) { $sql['orderby'] = "ORDER BY rand()"; } else { $sql['orderby'] = "ORDER BY {$orderby} {$order}"; } if (!empty($r['per_page']) && !empty($r['page']) && $r['per_page'] != -1) { $sql['pagination'] = $wpdb->prepare("LIMIT %d, %d", intval(($r['page'] - 1) * $r['per_page']), intval($r['per_page'])); } $where = ''; if (!empty($where_conditions)) { $sql['where'] = implode(' AND ', $where_conditions); $where = "WHERE {$sql['where']}"; } $paged_groups_sql = "{$sql['select']} FROM {$sql['from']} {$where} {$sql['orderby']} {$sql['pagination']}"; /** * Filters the pagination SQL statement. * * @since 1.5.0 * * @param string $value Concatenated SQL statement. * @param array $sql Array of SQL parts before concatenation. * @param array $r Array of parsed arguments for the get method. */ $paged_groups_sql = apply_filters('bp_groups_get_paged_groups_sql', $paged_groups_sql, $sql, $r); $cached = bp_core_get_incremented_cache($paged_groups_sql, 'bp_groups'); if (false === $cached) { $paged_group_ids = $wpdb->get_col($paged_groups_sql); bp_core_set_incremented_cache($paged_groups_sql, 'bp_groups', $paged_group_ids); } else { $paged_group_ids = $cached; } $uncached_group_ids = bp_get_non_cached_ids($paged_group_ids, 'bp_groups'); if ($uncached_group_ids) { $group_ids_sql = implode(',', array_map('intval', $uncached_group_ids)); $group_data_objects = $wpdb->get_results("SELECT g.* FROM {$bp->groups->table_name} g WHERE g.id IN ({$group_ids_sql})"); foreach ($group_data_objects as $group_data_object) { wp_cache_set($group_data_object->id, $group_data_object, 'bp_groups'); } } $paged_groups = array(); foreach ($paged_group_ids as $paged_group_id) { $paged_groups[] = new BP_Groups_Group($paged_group_id); } $total_groups_sql = "SELECT COUNT(DISTINCT g.id) FROM {$sql['from']} {$where}"; /** * Filters the SQL used to retrieve total group results. * * @since 1.5.0 * * @param string $t_sql Concatenated SQL statement used for retrieving total group results. * @param array $total_sql Array of SQL parts for the query. * @param array $r Array of parsed arguments for the get method. */ $total_groups_sql = apply_filters('bp_groups_get_total_groups_sql', $total_groups_sql, $sql, $r); $cached = bp_core_get_incremented_cache($total_groups_sql, 'bp_groups'); if (false === $cached) { $total_groups = (int) $wpdb->get_var($total_groups_sql); bp_core_set_incremented_cache($total_groups_sql, 'bp_groups', $total_groups); } else { $total_groups = (int) $cached; } $group_ids = array(); foreach ((array) $paged_groups as $group) { $group_ids[] = $group->id; } // Grab all groupmeta. if (!empty($r['update_meta_cache'])) { bp_groups_update_meta_cache($group_ids); } // Prefetch all administrator IDs, if requested. if ($r['update_admin_cache']) { BP_Groups_Member::prime_group_admins_mods_cache($group_ids); } // Set up integer properties needing casting. $int_props = array('id', 'creator_id', 'enable_forum'); // Integer casting. foreach ($paged_groups as $key => $g) { foreach ($int_props as $int_prop) { $paged_groups[$key]->{$int_prop} = (int) $paged_groups[$key]->{$int_prop}; } } unset($sql, $total_sql); return array('groups' => $paged_groups, 'total' => $total_groups); }