[ Index ]

PHP Cross Reference of BuddyPress

title

Body

[close]

/src/bp-groups/classes/ -> class-bp-group-member-query.php (source)

   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  }


Generated: Mon Oct 18 01:00:55 2021 Cross-referenced by PHPXref 0.7.1