[ Index ] |
PHP Cross Reference of BuddyPress |
[Summary view] [Print] [Text view]
1 <?php 2 /** 3 * BuddyPress Groups Classes. 4 * 5 * @package BuddyPress 6 * @subpackage GroupsClasses 7 * @since 1.8.0 8 */ 9 10 // Exit if accessed directly. 11 defined( 'ABSPATH' ) || exit; 12 13 /** 14 * Query for the members of a group. 15 * 16 * Special notes about the group members data schema: 17 * - *Members* are entries with is_confirmed = 1. 18 * - *Pending requests* are entries with is_confirmed = 0 and inviter_id = 0. 19 * - *Pending and sent invitations* are entries with is_confirmed = 0 and 20 * inviter_id != 0 and invite_sent = 1. 21 * - *Pending and unsent invitations* are entries with is_confirmed = 0 and 22 * inviter_id != 0 and invite_sent = 0. 23 * - *Membership requests* are entries with is_confirmed = 0 and 24 * inviter_id = 0 (and invite_sent = 0). 25 * 26 * @since 1.8.0 27 * @since 3.0.0 $group_id now supports multiple values. 28 * 29 * @param array $args { 30 * Array of arguments. Accepts all arguments from 31 * {@link BP_User_Query}, with the following additions: 32 * 33 * @type int|array|string $group_id ID of the group to limit results to. Also accepts multiple values 34 * either as an array or as a comma-delimited string. 35 * @type array $group_role Array of group roles to match ('member', 'mod', 'admin', 'banned'). 36 * Default: array( 'member' ). 37 * @type bool $is_confirmed Whether to limit to confirmed members. Default: true. 38 * @type string $type Sort order. Accepts any value supported by {@link BP_User_Query}, in 39 * addition to 'last_joined' and 'first_joined'. Default: 'last_joined'. 40 * } 41 */ 42 class BP_Group_Member_Query extends BP_User_Query { 43 44 /** 45 * Array of group member ids, cached to prevent redundant lookups. 46 * 47 * @since 1.8.1 48 * @var null|array Null if not yet defined, otherwise an array of ints. 49 */ 50 protected $group_member_ids; 51 52 /** 53 * Set up action hooks. 54 * 55 * @since 1.8.0 56 */ 57 public function setup_hooks() { 58 // Take this early opportunity to set the default 'type' param 59 // to 'last_joined', which will ensure that BP_User_Query 60 // trusts our order and does not try to apply its own. 61 if ( empty( $this->query_vars_raw['type'] ) ) { 62 $this->query_vars_raw['type'] = 'last_joined'; 63 } 64 65 // Set the sort order. 66 add_action( 'bp_pre_user_query', array( $this, 'set_orderby' ) ); 67 68 // Set up our populate_extras method. 69 add_action( 'bp_user_query_populate_extras', array( $this, 'populate_group_member_extras' ), 10, 2 ); 70 } 71 72 /** 73 * Get a list of user_ids to include in the IN clause of the main query. 74 * 75 * Overrides BP_User_Query::get_include_ids(), adding our additional 76 * group-member logic. 77 * 78 * @since 1.8.0 79 * 80 * @param array $include Existing group IDs in the $include parameter, 81 * as calculated in BP_User_Query. 82 * @return array 83 */ 84 public function get_include_ids( $include = array() ) { 85 // The following args are specific to group member queries, and 86 // are not present in the query_vars of a normal BP_User_Query. 87 // We loop through to make sure that defaults are set (though 88 // values passed to the constructor will, as usual, override 89 // these defaults). 90 $this->query_vars = bp_parse_args( 91 $this->query_vars, 92 array( 93 'group_id' => 0, 94 'group_role' => array( 'member' ), 95 'is_confirmed' => true, 96 'invite_sent' => null, 97 'inviter_id' => null, 98 'type' => 'last_joined', 99 ), 100 'bp_group_member_query_get_include_ids' 101 ); 102 103 $group_member_ids = $this->get_group_member_ids(); 104 105 // If the group member query returned no users, bail with an 106 // array that will guarantee no matches for BP_User_Query. 107 if ( empty( $group_member_ids ) ) { 108 return array( 0 ); 109 } 110 111 if ( ! empty( $include ) ) { 112 $group_member_ids = array_intersect( $include, $group_member_ids ); 113 } 114 115 return $group_member_ids; 116 } 117 118 /** 119 * Get the members of the queried group. 120 * 121 * @since 1.8.0 122 * 123 * @return array $ids User IDs of relevant group member ids. 124 */ 125 protected function get_group_member_ids() { 126 global $wpdb; 127 128 if ( is_array( $this->group_member_ids ) ) { 129 return $this->group_member_ids; 130 } 131 132 $bp = buddypress(); 133 $sql = array( 134 'select' => "SELECT user_id FROM {$bp->groups->table_name_members}", 135 'where' => array(), 136 'orderby' => '', 137 'order' => '', 138 ); 139 140 /* WHERE clauses *****************************************************/ 141 142 // Group id. 143 $group_ids = wp_parse_id_list( $this->query_vars['group_id'] ); 144 $group_ids = implode( ',', $group_ids ); 145 $sql['where'][] = "group_id IN ({$group_ids})"; 146 147 // If is_confirmed. 148 $is_confirmed = ! empty( $this->query_vars['is_confirmed'] ) ? 1 : 0; 149 $sql['where'][] = $wpdb->prepare( "is_confirmed = %d", $is_confirmed ); 150 151 // If invite_sent. 152 if ( ! is_null( $this->query_vars['invite_sent'] ) ) { 153 $invite_sent = ! empty( $this->query_vars['invite_sent'] ) ? 1 : 0; 154 $sql['where'][] = $wpdb->prepare( "invite_sent = %d", $invite_sent ); 155 } 156 157 // If inviter_id. 158 if ( ! is_null( $this->query_vars['inviter_id'] ) ) { 159 $inviter_id = $this->query_vars['inviter_id']; 160 161 // Empty: inviter_id = 0. (pass false, 0, or empty array). 162 if ( empty( $inviter_id ) ) { 163 $sql['where'][] = "inviter_id = 0"; 164 165 // The string 'any' matches any non-zero value (inviter_id != 0). 166 } elseif ( 'any' === $inviter_id ) { 167 $sql['where'][] = "inviter_id != 0"; 168 169 // Assume that a list of inviter IDs has been passed. 170 } else { 171 // Parse and sanitize. 172 $inviter_ids = wp_parse_id_list( $inviter_id ); 173 if ( ! empty( $inviter_ids ) ) { 174 $inviter_ids_sql = implode( ',', $inviter_ids ); 175 $sql['where'][] = "inviter_id IN ({$inviter_ids_sql})"; 176 } 177 } 178 } 179 180 // Role information is stored as follows: admins have 181 // is_admin = 1, mods have is_mod = 1, banned have is_banned = 182 // 1, and members have all three set to 0. 183 $roles = !empty( $this->query_vars['group_role'] ) ? $this->query_vars['group_role'] : array(); 184 if ( is_string( $roles ) ) { 185 $roles = explode( ',', $roles ); 186 } 187 188 // Sanitize: Only 'admin', 'mod', 'member', and 'banned' are valid. 189 $allowed_roles = array( 'admin', 'mod', 'member', 'banned' ); 190 foreach ( $roles as $role_key => $role_value ) { 191 if ( ! in_array( $role_value, $allowed_roles ) ) { 192 unset( $roles[ $role_key ] ); 193 } 194 } 195 196 $roles = array_unique( $roles ); 197 198 // When querying for a set of roles containing 'member' (for 199 // which there is no dedicated is_ column), figure out a list 200 // of columns *not* to match. 201 $roles_sql = ''; 202 if ( in_array( 'member', $roles ) ) { 203 $role_columns = array(); 204 foreach ( array_diff( $allowed_roles, $roles ) as $excluded_role ) { 205 $role_columns[] = 'is_' . $excluded_role . ' = 0'; 206 } 207 208 if ( ! empty( $role_columns ) ) { 209 $roles_sql = '(' . implode( ' AND ', $role_columns ) . ')'; 210 } 211 212 // When querying for a set of roles *not* containing 'member', 213 // simply construct a list of is_* = 1 clauses. 214 } else { 215 $role_columns = array(); 216 foreach ( $roles as $role ) { 217 $role_columns[] = 'is_' . $role . ' = 1'; 218 } 219 220 if ( ! empty( $role_columns ) ) { 221 $roles_sql = '(' . implode( ' OR ', $role_columns ) . ')'; 222 } 223 } 224 225 if ( ! empty( $roles_sql ) ) { 226 $sql['where'][] = $roles_sql; 227 } 228 229 $sql['where'] = ! empty( $sql['where'] ) ? 'WHERE ' . implode( ' AND ', $sql['where'] ) : ''; 230 231 // We fetch group members in order of last_joined, regardless 232 // of 'type'. If the 'type' value is not 'last_joined' or 233 // 'first_joined', the order will be overridden in 234 // BP_Group_Member_Query::set_orderby(). 235 $sql['orderby'] = "ORDER BY date_modified"; 236 $sql['order'] = 'first_joined' === $this->query_vars['type'] ? 'ASC' : 'DESC'; 237 238 $group_member_ids = $wpdb->get_col( "{$sql['select']} {$sql['where']} {$sql['orderby']} {$sql['order']}" ); 239 240 $invited_member_ids = array(); 241 242 // If appropriate, fetch invitations and add them to the results. 243 if ( ! $is_confirmed || ! is_null( $this->query_vars['invite_sent'] ) || ! is_null( $this->query_vars['inviter_id'] ) ) { 244 $invite_args = array( 245 'item_id' => $this->query_vars['group_id'], 246 'fields' => 'user_ids', 247 'type' => 'all', 248 ); 249 250 if ( ! is_null( $this->query_vars['invite_sent'] ) ) { 251 $invite_args['invite_sent'] = ! empty( $this->query_vars['invite_sent'] ) ? 'sent' : 'draft'; 252 } 253 254 // If inviter_id. 255 if ( ! is_null( $this->query_vars['inviter_id'] ) ) { 256 $inviter_id = $this->query_vars['inviter_id']; 257 258 // Empty: inviter_id = 0. (pass false, 0, or empty array). 259 if ( empty( $inviter_id ) ) { 260 $invite_args['type'] = 'request'; 261 262 /* 263 * The string 'any' matches any non-zero value (inviter_id != 0). 264 * These are invitations, not requests. 265 */ 266 } elseif ( 'any' === $inviter_id ) { 267 $invite_args['type'] = 'invite'; 268 269 // Assume that a list of inviter IDs has been passed. 270 } else { 271 $invite_args['type'] = 'invite'; 272 // Parse and sanitize. 273 $inviter_ids = wp_parse_id_list( $inviter_id ); 274 if ( ! empty( $inviter_ids ) ) { 275 $invite_args['inviter_id'] = $inviter_ids; 276 } 277 } 278 } 279 280 /* 281 * If first_joined is the "type" of query, sort the oldest 282 * requests and invitations to the top. 283 */ 284 if ( 'first_joined' === $this->query_vars['type'] ) { 285 $invite_args['order_by'] = 'date_modified'; 286 $invite_args['sort_order'] = 'ASC'; 287 } 288 289 $invited_member_ids = groups_get_invites( $invite_args ); 290 } 291 292 $this->group_member_ids = array_merge( $group_member_ids, $invited_member_ids ); 293 294 /** 295 * Filters the member IDs for the current group member query. 296 * 297 * Use this filter to build a custom query (such as when you've 298 * defined a custom 'type'). 299 * 300 * @since 2.0.0 301 * 302 * @param array $group_member_ids Array of associated member IDs. 303 * @param BP_Group_Member_Query $this Current BP_Group_Member_Query instance. 304 */ 305 $this->group_member_ids = apply_filters( 'bp_group_member_query_group_member_ids', $this->group_member_ids, $this ); 306 307 return $this->group_member_ids; 308 } 309 310 /** 311 * Tell BP_User_Query to order by the order of our query results. 312 * 313 * We only override BP_User_Query's native ordering in case of the 314 * 'last_joined' and 'first_joined' $type parameters. 315 * 316 * @since 1.8.1 317 * 318 * @param BP_User_Query $query BP_User_Query object. 319 */ 320 public function set_orderby( $query ) { 321 $gm_ids = $this->get_group_member_ids(); 322 if ( empty( $gm_ids ) ) { 323 $gm_ids = array( 0 ); 324 } 325 326 // For 'last_joined', 'first_joined', and 'group_activity' 327 // types, we override the default orderby clause of 328 // BP_User_Query. In the case of 'group_activity', we perform 329 // a separate query to get the necessary order. In the case of 330 // 'last_joined' and 'first_joined', we can trust the order of 331 // results from BP_Group_Member_Query::get_group_members(). 332 // In all other cases, we fall through and let BP_User_Query 333 // do its own (non-group-specific) ordering. 334 if ( in_array( $query->query_vars['type'], array( 'last_joined', 'first_joined', 'group_activity' ) ) ) { 335 336 // Group Activity DESC. 337 if ( 'group_activity' == $query->query_vars['type'] ) { 338 $gm_ids = $this->get_gm_ids_ordered_by_activity( $query, $gm_ids ); 339 } 340 341 // The first param in the FIELD() clause is the sort column id. 342 $gm_ids = array_merge( array( 'u.id' ), wp_parse_id_list( $gm_ids ) ); 343 $gm_ids_sql = implode( ',', $gm_ids ); 344 345 $query->uid_clauses['orderby'] = "ORDER BY FIELD(" . $gm_ids_sql . ")"; 346 } 347 348 // Prevent this filter from running on future BP_User_Query 349 // instances on the same page. 350 remove_action( 'bp_pre_user_query', array( $this, 'set_orderby' ) ); 351 } 352 353 /** 354 * Fetch additional data required in bp_group_has_members() loops. 355 * 356 * Additional data fetched: 357 * - is_banned 358 * - date_modified 359 * 360 * @since 1.8.0 361 * 362 * @param BP_User_Query $query BP_User_Query object. Because we're 363 * filtering the current object, we use 364 * $this inside of the method instead. 365 * @param string $user_ids_sql Sanitized, comma-separated string of 366 * the user ids returned by the main query. 367 */ 368 public function populate_group_member_extras( $query, $user_ids_sql ) { 369 global $wpdb; 370 371 $bp = buddypress(); 372 $extras = $wpdb->get_results( $wpdb->prepare( "SELECT id, user_id, date_modified, is_admin, is_mod, comments, user_title, invite_sent, is_confirmed, inviter_id, is_banned FROM {$bp->groups->table_name_members} WHERE user_id IN ({$user_ids_sql}) AND group_id = %d", $this->query_vars['group_id'] ) ); 373 374 foreach ( (array) $extras as $extra ) { 375 if ( isset( $this->results[ $extra->user_id ] ) ) { 376 // The user_id is provided for backward compatibility. 377 $this->results[ $extra->user_id ]->user_id = (int) $extra->user_id; 378 $this->results[ $extra->user_id ]->is_admin = (int) $extra->is_admin; 379 $this->results[ $extra->user_id ]->is_mod = (int) $extra->is_mod; 380 $this->results[ $extra->user_id ]->is_banned = (int) $extra->is_banned; 381 $this->results[ $extra->user_id ]->date_modified = $extra->date_modified; 382 $this->results[ $extra->user_id ]->user_title = $extra->user_title; 383 $this->results[ $extra->user_id ]->comments = $extra->comments; 384 $this->results[ $extra->user_id ]->invite_sent = (int) $extra->invite_sent; 385 $this->results[ $extra->user_id ]->inviter_id = (int) $extra->inviter_id; 386 $this->results[ $extra->user_id ]->is_confirmed = (int) $extra->is_confirmed; 387 $this->results[ $extra->user_id ]->membership_id = (int) $extra->id; 388 } 389 } 390 391 // Add accurate invitation info from the invitations table. 392 $invites = groups_get_invites( array( 393 'user_id' => $user_ids_sql, 394 'item_id' => $this->query_vars['group_id'], 395 'type' => 'all', 396 ) ); 397 foreach ( $invites as $invite ) { 398 if ( isset( $this->results[ $invite->user_id ] ) ) { 399 $this->results[ $invite->user_id ]->comments = $invite->content; 400 $this->results[ $invite->user_id ]->is_confirmed = 0; 401 $this->results[ $invite->user_id ]->invitation_id = $invite->id; 402 $this->results[ $invite->user_id ]->invite_sent = (int) $invite->invite_sent; 403 $this->results[ $invite->user_id ]->inviter_id = $invite->inviter_id; 404 405 // Backfill properties that are not being set above. 406 if ( ! isset( $this->results[ $invite->user_id ]->user_id ) ) { 407 $this->results[ $invite->user_id ]->user_id = $invite->user_id; 408 } 409 if ( ! isset( $this->results[ $invite->user_id ]->is_admin ) ) { 410 $this->results[ $invite->user_id ]->is_admin = 0; 411 } 412 if ( ! isset( $this->results[ $invite->user_id ]->is_mod ) ) { 413 $this->results[ $invite->user_id ]->is_mod = 0; 414 } 415 if ( ! isset( $this->results[ $invite->user_id ]->is_banned ) ) { 416 $this->results[ $invite->user_id ]->is_banned = 0; 417 } 418 if ( ! isset( $this->results[ $invite->user_id ]->date_modified ) ) { 419 $this->results[ $invite->user_id ]->date_modified = $invite->date_modified; 420 } 421 if ( ! isset( $this->results[ $invite->user_id ]->user_title ) ) { 422 $this->results[ $invite->user_id ]->user_title = ''; 423 } 424 if ( ! isset( $this->results[ $invite->user_id ]->membership_id ) ) { 425 $this->results[ $invite->user_id ]->membership_id = 0; 426 } 427 } 428 } 429 430 // Don't filter other BP_User_Query objects on the same page. 431 remove_action( 'bp_user_query_populate_extras', array( $this, 'populate_group_member_extras' ), 10 ); 432 } 433 434 /** 435 * Sort user IDs by how recently they have generated activity within a given group. 436 * 437 * @since 2.1.0 438 * 439 * @param BP_User_Query $query BP_User_Query object. 440 * @param array $gm_ids array of group member ids. 441 * @return array 442 */ 443 public function get_gm_ids_ordered_by_activity( $query, $gm_ids = array() ) { 444 global $wpdb; 445 446 if ( empty( $gm_ids ) ) { 447 return $gm_ids; 448 } 449 450 if ( ! bp_is_active( 'activity' ) ) { 451 return $gm_ids; 452 } 453 454 $activity_table = buddypress()->activity->table_name; 455 456 $sql = array( 457 'select' => "SELECT user_id, max( date_recorded ) as date_recorded FROM {$activity_table}", 458 'where' => array(), 459 'groupby' => 'GROUP BY user_id', 460 'orderby' => 'ORDER BY date_recorded', 461 'order' => 'DESC', 462 ); 463 464 $sql['where'] = array( 465 'user_id IN (' . implode( ',', wp_parse_id_list( $gm_ids ) ) . ')', 466 'item_id = ' . absint( $query->query_vars['group_id'] ), 467 $wpdb->prepare( "component = %s", buddypress()->groups->id ), 468 ); 469 470 $sql['where'] = 'WHERE ' . implode( ' AND ', $sql['where'] ); 471 472 $group_user_ids = $wpdb->get_results( "{$sql['select']} {$sql['where']} {$sql['groupby']} {$sql['orderby']} {$sql['order']}" ); 473 474 return wp_list_pluck( $group_user_ids, 'user_id' ); 475 } 476 }
title
Description
Body
title
Description
Body
title
Description
Body
title
Body
Generated: Tue Oct 15 01:00:54 2024 | Cross-referenced by PHPXref 0.7.1 |