[ 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( $this->query_vars, array(
  91              'group_id'     => 0,
  92              'group_role'   => array( 'member' ),
  93              'is_confirmed' => true,
  94              'invite_sent'  => null,
  95              'inviter_id'   => null,
  96              'type'         => 'last_joined',
  97          ), 'bp_group_member_query_get_include_ids' );
  98  
  99          $group_member_ids = $this->get_group_member_ids();
 100  
 101          // If the group member query returned no users, bail with an
 102          // array that will guarantee no matches for BP_User_Query.
 103          if ( empty( $group_member_ids ) ) {
 104              return array( 0 );
 105          }
 106  
 107          if ( ! empty( $include ) ) {
 108              $group_member_ids = array_intersect( $include, $group_member_ids );
 109          }
 110  
 111          return $group_member_ids;
 112      }
 113  
 114      /**
 115       * Get the members of the queried group.
 116       *
 117       * @since 1.8.0
 118       *
 119       * @return array $ids User IDs of relevant group member ids.
 120       */
 121  	protected function get_group_member_ids() {
 122          global $wpdb;
 123  
 124          if ( is_array( $this->group_member_ids ) ) {
 125              return $this->group_member_ids;
 126          }
 127  
 128          $bp  = buddypress();
 129          $sql = array(
 130              'select'  => "SELECT user_id FROM {$bp->groups->table_name_members}",
 131              'where'   => array(),
 132              'orderby' => '',
 133              'order'   => '',
 134          );
 135  
 136          /* WHERE clauses *****************************************************/
 137  
 138          // Group id.
 139          $group_ids = wp_parse_id_list( $this->query_vars['group_id'] );
 140          $group_ids = implode( ',', $group_ids );
 141          $sql['where'][] = "group_id IN ({$group_ids})";
 142  
 143          // If is_confirmed.
 144          $is_confirmed = ! empty( $this->query_vars['is_confirmed'] ) ? 1 : 0;
 145          $sql['where'][] = $wpdb->prepare( "is_confirmed = %d", $is_confirmed );
 146  
 147          // If invite_sent.
 148          if ( ! is_null( $this->query_vars['invite_sent'] ) ) {
 149              $invite_sent = ! empty( $this->query_vars['invite_sent'] ) ? 1 : 0;
 150              $sql['where'][] = $wpdb->prepare( "invite_sent = %d", $invite_sent );
 151          }
 152  
 153          // If inviter_id.
 154          if ( ! is_null( $this->query_vars['inviter_id'] ) ) {
 155              $inviter_id = $this->query_vars['inviter_id'];
 156  
 157              // Empty: inviter_id = 0. (pass false, 0, or empty array).
 158              if ( empty( $inviter_id ) ) {
 159                  $sql['where'][] = "inviter_id = 0";
 160  
 161              // The string 'any' matches any non-zero value (inviter_id != 0).
 162              } elseif ( 'any' === $inviter_id ) {
 163                  $sql['where'][] = "inviter_id != 0";
 164  
 165              // Assume that a list of inviter IDs has been passed.
 166              } else {
 167                  // Parse and sanitize.
 168                  $inviter_ids = wp_parse_id_list( $inviter_id );
 169                  if ( ! empty( $inviter_ids ) ) {
 170                      $inviter_ids_sql = implode( ',', $inviter_ids );
 171                      $sql['where'][] = "inviter_id IN ({$inviter_ids_sql})";
 172                  }
 173              }
 174          }
 175  
 176          // Role information is stored as follows: admins have
 177          // is_admin = 1, mods have is_mod = 1, banned have is_banned =
 178          // 1, and members have all three set to 0.
 179          $roles = !empty( $this->query_vars['group_role'] ) ? $this->query_vars['group_role'] : array();
 180          if ( is_string( $roles ) ) {
 181              $roles = explode( ',', $roles );
 182          }
 183  
 184          // Sanitize: Only 'admin', 'mod', 'member', and 'banned' are valid.
 185          $allowed_roles = array( 'admin', 'mod', 'member', 'banned' );
 186          foreach ( $roles as $role_key => $role_value ) {
 187              if ( ! in_array( $role_value, $allowed_roles ) ) {
 188                  unset( $roles[ $role_key ] );
 189              }
 190          }
 191  
 192          $roles = array_unique( $roles );
 193  
 194          // When querying for a set of roles containing 'member' (for
 195          // which there is no dedicated is_ column), figure out a list
 196          // of columns *not* to match.
 197          $roles_sql = '';
 198          if ( in_array( 'member', $roles ) ) {
 199              $role_columns = array();
 200              foreach ( array_diff( $allowed_roles, $roles ) as $excluded_role ) {
 201                  $role_columns[] = 'is_' . $excluded_role . ' = 0';
 202              }
 203  
 204              if ( ! empty( $role_columns ) ) {
 205                  $roles_sql = '(' . implode( ' AND ', $role_columns ) . ')';
 206              }
 207  
 208          // When querying for a set of roles *not* containing 'member',
 209          // simply construct a list of is_* = 1 clauses.
 210          } else {
 211              $role_columns = array();
 212              foreach ( $roles as $role ) {
 213                  $role_columns[] = 'is_' . $role . ' = 1';
 214              }
 215  
 216              if ( ! empty( $role_columns ) ) {
 217                  $roles_sql = '(' . implode( ' OR ', $role_columns ) . ')';
 218              }
 219          }
 220  
 221          if ( ! empty( $roles_sql ) ) {
 222              $sql['where'][] = $roles_sql;
 223          }
 224  
 225          $sql['where'] = ! empty( $sql['where'] ) ? 'WHERE ' . implode( ' AND ', $sql['where'] ) : '';
 226  
 227          // We fetch group members in order of last_joined, regardless
 228          // of 'type'. If the 'type' value is not 'last_joined' or
 229          // 'first_joined', the order will be overridden in
 230          // BP_Group_Member_Query::set_orderby().
 231          $sql['orderby'] = "ORDER BY date_modified";
 232          $sql['order']   = 'first_joined' === $this->query_vars['type'] ? 'ASC' : 'DESC';
 233  
 234          $group_member_ids = $wpdb->get_col( "{$sql['select']} {$sql['where']} {$sql['orderby']} {$sql['order']}" );
 235  
 236          $invited_member_ids = array();
 237  
 238          // If appropriate, fetch invitations and add them to the results.
 239          if ( ! $is_confirmed || ! is_null( $this->query_vars['invite_sent'] ) || ! is_null( $this->query_vars['inviter_id'] ) ) {
 240              $invite_args = array(
 241                  'item_id' => $this->query_vars['group_id'],
 242                  'fields'  => 'user_ids',
 243                  'type'    => 'all',
 244              );
 245  
 246              if ( ! is_null( $this->query_vars['invite_sent'] ) ) {
 247                  $invite_args['invite_sent'] = ! empty( $this->query_vars['invite_sent'] ) ? 'sent' : 'draft';
 248              }
 249  
 250              // If inviter_id.
 251              if ( ! is_null( $this->query_vars['inviter_id'] ) ) {
 252                  $inviter_id = $this->query_vars['inviter_id'];
 253  
 254                  // Empty: inviter_id = 0. (pass false, 0, or empty array).
 255                  if ( empty( $inviter_id ) ) {
 256                      $invite_args['type'] = 'request';
 257  
 258                  /*
 259                  * The string 'any' matches any non-zero value (inviter_id != 0).
 260                  * These are invitations, not requests.
 261                  */
 262                  } elseif ( 'any' === $inviter_id ) {
 263                      $invite_args['type'] = 'invite';
 264  
 265                  // Assume that a list of inviter IDs has been passed.
 266                  } else {
 267                      $invite_args['type'] = 'invite';
 268                      // Parse and sanitize.
 269                      $inviter_ids = wp_parse_id_list( $inviter_id );
 270                      if ( ! empty( $inviter_ids ) ) {
 271                          $invite_args['inviter_id'] = $inviter_ids;
 272                      }
 273                  }
 274              }
 275  
 276              /*
 277               * If first_joined is the "type" of query, sort the oldest
 278               * requests and invitations to the top.
 279               */
 280              if ( 'first_joined' === $this->query_vars['type'] ) {
 281                  $invite_args['order_by']   = 'date_modified';
 282                  $invite_args['sort_order'] = 'ASC';
 283              }
 284  
 285              $invited_member_ids = groups_get_invites( $invite_args );
 286          }
 287  
 288          $this->group_member_ids = array_merge( $group_member_ids, $invited_member_ids );
 289  
 290          /**
 291           * Filters the member IDs for the current group member query.
 292           *
 293           * Use this filter to build a custom query (such as when you've
 294           * defined a custom 'type').
 295           *
 296           * @since 2.0.0
 297           *
 298           * @param array                 $group_member_ids Array of associated member IDs.
 299           * @param BP_Group_Member_Query $this             Current BP_Group_Member_Query instance.
 300           */
 301          $this->group_member_ids = apply_filters( 'bp_group_member_query_group_member_ids', $this->group_member_ids, $this );
 302  
 303          return $this->group_member_ids;
 304      }
 305  
 306      /**
 307       * Tell BP_User_Query to order by the order of our query results.
 308       *
 309       * We only override BP_User_Query's native ordering in case of the
 310       * 'last_joined' and 'first_joined' $type parameters.
 311       *
 312       * @since 1.8.1
 313       *
 314       * @param BP_User_Query $query BP_User_Query object.
 315       */
 316  	public function set_orderby( $query ) {
 317          $gm_ids = $this->get_group_member_ids();
 318          if ( empty( $gm_ids ) ) {
 319              $gm_ids = array( 0 );
 320          }
 321  
 322          // For 'last_joined', 'first_joined', and 'group_activity'
 323          // types, we override the default orderby clause of
 324          // BP_User_Query. In the case of 'group_activity', we perform
 325          // a separate query to get the necessary order. In the case of
 326          // 'last_joined' and 'first_joined', we can trust the order of
 327          // results from  BP_Group_Member_Query::get_group_members().
 328          // In all other cases, we fall through and let BP_User_Query
 329          // do its own (non-group-specific) ordering.
 330          if ( in_array( $query->query_vars['type'], array( 'last_joined', 'first_joined', 'group_activity' ) ) ) {
 331  
 332              // Group Activity DESC.
 333              if ( 'group_activity' == $query->query_vars['type'] ) {
 334                  $gm_ids = $this->get_gm_ids_ordered_by_activity( $query, $gm_ids );
 335              }
 336  
 337              // The first param in the FIELD() clause is the sort column id.
 338              $gm_ids = array_merge( array( 'u.id' ), wp_parse_id_list( $gm_ids ) );
 339              $gm_ids_sql = implode( ',', $gm_ids );
 340  
 341              $query->uid_clauses['orderby'] = "ORDER BY FIELD(" . $gm_ids_sql . ")";
 342          }
 343  
 344          // Prevent this filter from running on future BP_User_Query
 345          // instances on the same page.
 346          remove_action( 'bp_pre_user_query', array( $this, 'set_orderby' ) );
 347      }
 348  
 349      /**
 350       * Fetch additional data required in bp_group_has_members() loops.
 351       *
 352       * Additional data fetched:
 353       *      - is_banned
 354       *      - date_modified
 355       *
 356       * @since 1.8.0
 357       *
 358       * @param BP_User_Query $query        BP_User_Query object. Because we're
 359       *                                    filtering the current object, we use
 360       *                                    $this inside of the method instead.
 361       * @param string        $user_ids_sql Sanitized, comma-separated string of
 362       *                                    the user ids returned by the main query.
 363       */
 364  	public function populate_group_member_extras( $query, $user_ids_sql ) {
 365          global $wpdb;
 366  
 367          $bp     = buddypress();
 368          $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'] ) );
 369  
 370          foreach ( (array) $extras as $extra ) {
 371              if ( isset( $this->results[ $extra->user_id ] ) ) {
 372                  // The user_id is provided for backward compatibility.
 373                  $this->results[ $extra->user_id ]->user_id       = (int) $extra->user_id;
 374                  $this->results[ $extra->user_id ]->is_admin      = (int) $extra->is_admin;
 375                  $this->results[ $extra->user_id ]->is_mod        = (int) $extra->is_mod;
 376                  $this->results[ $extra->user_id ]->is_banned     = (int) $extra->is_banned;
 377                  $this->results[ $extra->user_id ]->date_modified = $extra->date_modified;
 378                  $this->results[ $extra->user_id ]->user_title    = $extra->user_title;
 379                  $this->results[ $extra->user_id ]->comments      = $extra->comments;
 380                  $this->results[ $extra->user_id ]->invite_sent   = (int) $extra->invite_sent;
 381                  $this->results[ $extra->user_id ]->inviter_id    = (int) $extra->inviter_id;
 382                  $this->results[ $extra->user_id ]->is_confirmed  = (int) $extra->is_confirmed;
 383                  $this->results[ $extra->user_id ]->membership_id = (int) $extra->id;
 384              }
 385          }
 386  
 387          // Add accurate invitation info from the invitations table.
 388          $invites = groups_get_invites( array(
 389              'user_id' => $user_ids_sql,
 390              'item_id' => $this->query_vars['group_id'],
 391              'type'    => 'all',
 392          ) );
 393          foreach ( $invites as $invite ) {
 394              if ( isset( $this->results[ $invite->user_id ] ) ) {
 395                  $this->results[ $invite->user_id ]->comments      = $invite->content;
 396                  $this->results[ $invite->user_id ]->is_confirmed  = 0;
 397                  $this->results[ $invite->user_id ]->invitation_id = $invite->id;
 398                  $this->results[ $invite->user_id ]->invite_sent   = (int) $invite->invite_sent;
 399                  $this->results[ $invite->user_id ]->inviter_id    = $invite->inviter_id;
 400  
 401                  // Backfill properties that are not being set above.
 402                  if ( ! isset( $this->results[ $invite->user_id ]->user_id ) ) {
 403                      $this->results[ $invite->user_id ]->user_id = $invite->user_id;
 404                  }
 405                  if ( ! isset( $this->results[ $invite->user_id ]->is_admin ) ) {
 406                      $this->results[ $invite->user_id ]->is_admin = 0;
 407                  }
 408                  if ( ! isset( $this->results[ $invite->user_id ]->is_mod ) ) {
 409                      $this->results[ $invite->user_id ]->is_mod = 0;
 410                  }
 411                  if ( ! isset( $this->results[ $invite->user_id ]->is_banned ) ) {
 412                      $this->results[ $invite->user_id ]->is_banned = 0;
 413                  }
 414                  if ( ! isset( $this->results[ $invite->user_id ]->date_modified ) ) {
 415                      $this->results[ $invite->user_id ]->date_modified = $invite->date_modified;
 416                  }
 417                  if ( ! isset( $this->results[ $invite->user_id ]->user_title ) ) {
 418                      $this->results[ $invite->user_id ]->user_title = '';
 419                  }
 420                  if ( ! isset( $this->results[ $invite->user_id ]->membership_id ) ) {
 421                      $this->results[ $invite->user_id ]->membership_id = 0;
 422                  }
 423              }
 424          }
 425  
 426          // Don't filter other BP_User_Query objects on the same page.
 427          remove_action( 'bp_user_query_populate_extras', array( $this, 'populate_group_member_extras' ), 10 );
 428      }
 429  
 430      /**
 431       * Sort user IDs by how recently they have generated activity within a given group.
 432       *
 433       * @since 2.1.0
 434       *
 435       * @param BP_User_Query $query  BP_User_Query object.
 436       * @param array         $gm_ids array of group member ids.
 437       * @return array
 438       */
 439  	public function get_gm_ids_ordered_by_activity( $query, $gm_ids = array() ) {
 440          global $wpdb;
 441  
 442          if ( empty( $gm_ids ) ) {
 443              return $gm_ids;
 444          }
 445  
 446          if ( ! bp_is_active( 'activity' ) ) {
 447              return $gm_ids;
 448          }
 449  
 450          $activity_table = buddypress()->activity->table_name;
 451  
 452          $sql = array(
 453              'select'  => "SELECT user_id, max( date_recorded ) as date_recorded FROM {$activity_table}",
 454              'where'   => array(),
 455              'groupby' => 'GROUP BY user_id',
 456              'orderby' => 'ORDER BY date_recorded',
 457              'order'   => 'DESC',
 458          );
 459  
 460          $sql['where'] = array(
 461              'user_id IN (' . implode( ',', wp_parse_id_list( $gm_ids ) ) . ')',
 462              'item_id = ' . absint( $query->query_vars['group_id'] ),
 463              $wpdb->prepare( "component = %s", buddypress()->groups->id ),
 464          );
 465  
 466          $sql['where'] = 'WHERE ' . implode( ' AND ', $sql['where'] );
 467  
 468          $group_user_ids = $wpdb->get_results( "{$sql['select']} {$sql['where']} {$sql['groupby']} {$sql['orderby']} {$sql['order']}" );
 469  
 470          return wp_list_pluck( $group_user_ids, 'user_id' );
 471      }
 472  }


Generated: Sat Sep 21 01:01:46 2019 Cross-referenced by PHPXref 0.7.1