Changeset 9485 for trunk/src/bp-groups/bp-groups-classes.php
- Timestamp:
- 02/15/2015 12:48:56 AM (11 years ago)
- File:
-
- 1 edited
-
trunk/src/bp-groups/bp-groups-classes.php (modified) (2 diffs)
Legend:
- Unmodified
- Added
- Removed
-
trunk/src/bp-groups/bp-groups-classes.php
r9471 r9485 1 1 <?php 2 3 2 /** 4 3 * BuddyPress Groups Classes … … 11 10 defined( 'ABSPATH' ) || exit; 12 11 13 /** 14 * BuddyPress Group object. 15 */ 16 class BP_Groups_Group { 17 18 /** 19 * ID of the group. 20 * 21 * @access public 22 * @var int 23 */ 24 public $id; 25 26 /** 27 * User ID of the group's creator. 28 * 29 * @access public 30 * @var int 31 */ 32 public $creator_id; 33 34 /** 35 * Name of the group. 36 * 37 * @access public 38 * @var string 39 */ 40 public $name; 41 42 /** 43 * Group slug. 44 * 45 * @access public 46 * @var string 47 */ 48 public $slug; 49 50 /** 51 * Group description. 52 * 53 * @access public 54 * @var string 55 */ 56 public $description; 57 58 /** 59 * Group status. 60 * 61 * Core statuses are 'public', 'private', and 'hidden'. 62 * 63 * @access public 64 * @var string 65 */ 66 public $status; 67 68 /** 69 * Should (legacy) bbPress forums be enabled for this group? 70 * 71 * @access public 72 * @var int 73 */ 74 public $enable_forum; 75 76 /** 77 * Date the group was created. 78 * 79 * @access public 80 * @var string 81 */ 82 public $date_created; 83 84 /** 85 * Data about the group's admins. 86 * 87 * @access public 88 * @var array 89 */ 90 public $admins; 91 92 /** 93 * Data about the group's moderators. 94 * 95 * @access public 96 * @var array 97 */ 98 public $mods; 99 100 /** 101 * Total count of group members. 102 * 103 * @access public 104 * @var int 105 */ 106 public $total_member_count; 107 108 /** 109 * Is the current user a member of this group? 110 * 111 * @since BuddyPress (1.2.0) 112 * @var bool 113 */ 114 public $is_member; 115 116 /** 117 * Does the current user have an outstanding invitation to this group? 118 * 119 * @since BuddyPress (1.9.0) 120 * @var bool 121 */ 122 public $is_invited; 123 124 /** 125 * Does the current user have a pending membership request to this group? 126 * 127 * @since BuddyPress (1.9.0) 128 * @var bool 129 */ 130 public $is_pending; 131 132 /** 133 * Timestamp of the last activity that happened in this group. 134 * 135 * @since BuddyPress (1.2.0) 136 * @var string 137 */ 138 public $last_activity; 139 140 /** 141 * If this is a private or hidden group, does the current user have access? 142 * 143 * @since BuddyPress (1.6.0) 144 * @var bool 145 */ 146 public $user_has_access; 147 148 /** 149 * Raw arguments passed to the constructor. 150 * 151 * @since BuddyPress (2.0.0) 152 * @var array 153 */ 154 public $args; 155 156 /** 157 * Constructor method. 158 * 159 * @param int $id Optional. If the ID of an existing group is provided, 160 * the object will be pre-populated with info about that group. 161 * @param array $args { 162 * Array of optional arguments. 163 * @type bool $populate_extras Whether to fetch "extra" data about 164 * the group (group admins/mods, access for the current user). 165 * Default: false. 166 * } 167 */ 168 public function __construct( $id = null, $args = array() ) { 169 $this->args = wp_parse_args( $args, array( 170 'populate_extras' => false, 171 ) ); 172 173 if ( !empty( $id ) ) { 174 $this->id = $id; 175 $this->populate(); 176 } 177 } 178 179 /** 180 * Set up data about the current group. 181 */ 182 public function populate() { 183 global $wpdb; 184 185 // Get BuddyPress 186 $bp = buddypress(); 187 188 // Check cache for group data 189 $group = wp_cache_get( $this->id, 'bp_groups' ); 190 191 // Cache missed, so query the DB 192 if ( false === $group ) { 193 $group = $wpdb->get_row( $wpdb->prepare( "SELECT g.* FROM {$bp->groups->table_name} g WHERE g.id = %d", $this->id ) ); 194 195 wp_cache_set( $this->id, $group, 'bp_groups' ); 196 } 197 198 // No group found so set the ID and bail 199 if ( empty( $group ) || is_wp_error( $group ) ) { 200 $this->id = 0; 201 return; 202 } 203 204 // Group found so setup the object variables 205 $this->id = $group->id; 206 $this->creator_id = $group->creator_id; 207 $this->name = stripslashes( $group->name ); 208 $this->slug = $group->slug; 209 $this->description = stripslashes( $group->description ); 210 $this->status = $group->status; 211 $this->enable_forum = $group->enable_forum; 212 $this->date_created = $group->date_created; 213 214 // Are we getting extra group data? 215 if ( ! empty( $this->args['populate_extras'] ) ) { 216 217 // Get group admins and mods 218 $admin_mods = $wpdb->get_results( apply_filters( 'bp_group_admin_mods_user_join_filter', $wpdb->prepare( "SELECT u.ID as user_id, u.user_login, u.user_email, u.user_nicename, m.is_admin, m.is_mod FROM {$wpdb->users} u, {$bp->groups->table_name_members} m WHERE u.ID = m.user_id AND m.group_id = %d AND ( m.is_admin = 1 OR m.is_mod = 1 )", $this->id ) ) ); 219 220 // Add admins and moderators to their respective arrays 221 foreach ( (array) $admin_mods as $user ) { 222 if ( !empty( $user->is_admin ) ) { 223 $this->admins[] = $user; 224 } else { 225 $this->mods[] = $user; 226 } 227 } 228 229 // Set up some specific group vars from meta. Excluded 230 // from the bp_groups cache because it's cached independently 231 $this->last_activity = groups_get_groupmeta( $this->id, 'last_activity' ); 232 $this->total_member_count = groups_get_groupmeta( $this->id, 'total_member_count' ); 233 234 // Set user-specific data 235 $user_id = bp_loggedin_user_id(); 236 $this->is_member = BP_Groups_Member::check_is_member( $user_id, $this->id ); 237 $this->is_invited = BP_Groups_Member::check_has_invite( $user_id, $this->id ); 238 $this->is_pending = BP_Groups_Member::check_for_membership_request( $user_id, $this->id ); 239 240 // If this is a private or hidden group, does the current user have access? 241 if ( ( 'private' === $this->status ) || ( 'hidden' === $this->status ) ) { 242 243 // Assume user does not have access to hidden/private groups 244 $this->user_has_access = false; 245 246 // Group members or community moderators have access 247 if ( ( $this->is_member && is_user_logged_in() ) || bp_current_user_can( 'bp_moderate' ) ) { 248 $this->user_has_access = true; 249 } 250 } else { 251 $this->user_has_access = true; 252 } 253 } 254 } 255 256 /** 257 * Save the current group to the database. 258 * 259 * @return bool True on success, false on failure. 260 */ 261 public function save() { 262 global $wpdb; 263 264 $bp = buddypress(); 265 266 $this->creator_id = apply_filters( 'groups_group_creator_id_before_save', $this->creator_id, $this->id ); 267 $this->name = apply_filters( 'groups_group_name_before_save', $this->name, $this->id ); 268 $this->slug = apply_filters( 'groups_group_slug_before_save', $this->slug, $this->id ); 269 $this->description = apply_filters( 'groups_group_description_before_save', $this->description, $this->id ); 270 $this->status = apply_filters( 'groups_group_status_before_save', $this->status, $this->id ); 271 $this->enable_forum = apply_filters( 'groups_group_enable_forum_before_save', $this->enable_forum, $this->id ); 272 $this->date_created = apply_filters( 'groups_group_date_created_before_save', $this->date_created, $this->id ); 273 274 do_action_ref_array( 'groups_group_before_save', array( &$this ) ); 275 276 // Groups need at least a name 277 if ( empty( $this->name ) ) { 278 return false; 279 } 280 281 // Set slug with group title if not passed 282 if ( empty( $this->slug ) ) { 283 $this->slug = sanitize_title( $this->name ); 284 } 285 286 // Sanity check 287 if ( empty( $this->slug ) ) { 288 return false; 289 } 290 291 // Check for slug conflicts if creating new group 292 if ( empty( $this->id ) ) { 293 $this->slug = groups_check_slug( $this->slug ); 294 } 295 296 if ( !empty( $this->id ) ) { 297 $sql = $wpdb->prepare( 298 "UPDATE {$bp->groups->table_name} SET 299 creator_id = %d, 300 name = %s, 301 slug = %s, 302 description = %s, 303 status = %s, 304 enable_forum = %d, 305 date_created = %s 306 WHERE 307 id = %d 308 ", 309 $this->creator_id, 310 $this->name, 311 $this->slug, 312 $this->description, 313 $this->status, 314 $this->enable_forum, 315 $this->date_created, 316 $this->id 317 ); 318 } else { 319 $sql = $wpdb->prepare( 320 "INSERT INTO {$bp->groups->table_name} ( 321 creator_id, 322 name, 323 slug, 324 description, 325 status, 326 enable_forum, 327 date_created 328 ) VALUES ( 329 %d, %s, %s, %s, %s, %d, %s 330 )", 331 $this->creator_id, 332 $this->name, 333 $this->slug, 334 $this->description, 335 $this->status, 336 $this->enable_forum, 337 $this->date_created 338 ); 339 } 340 341 if ( false === $wpdb->query($sql) ) 342 return false; 343 344 if ( empty( $this->id ) ) 345 $this->id = $wpdb->insert_id; 346 347 do_action_ref_array( 'groups_group_after_save', array( &$this ) ); 348 349 wp_cache_delete( $this->id, 'bp_groups' ); 350 351 return true; 352 } 353 354 /** 355 * Delete the current group. 356 * 357 * @return bool True on success, false on failure. 358 */ 359 public function delete() { 360 global $wpdb; 361 362 // Delete groupmeta for the group 363 groups_delete_groupmeta( $this->id ); 364 365 // Fetch the user IDs of all the members of the group 366 $user_ids = BP_Groups_Member::get_group_member_ids( $this->id ); 367 $user_id_str = esc_sql( implode( ',', wp_parse_id_list( $user_ids ) ) ); 368 369 // Modify group count usermeta for members 370 $wpdb->query( "UPDATE {$wpdb->usermeta} SET meta_value = meta_value - 1 WHERE meta_key = 'total_group_count' AND user_id IN ( {$user_id_str} )" ); 371 372 // Now delete all group member entries 373 BP_Groups_Member::delete_all( $this->id ); 374 375 do_action_ref_array( 'bp_groups_delete_group', array( &$this, $user_ids ) ); 376 377 wp_cache_delete( $this->id, 'bp_groups' ); 378 379 $bp = buddypress(); 380 381 // Finally remove the group entry from the DB 382 if ( !$wpdb->query( $wpdb->prepare( "DELETE FROM {$bp->groups->table_name} WHERE id = %d", $this->id ) ) ) 383 return false; 384 385 return true; 386 } 387 388 /** Static Methods ****************************************************/ 389 390 /** 391 * Get whether a group exists for a given slug. 392 * 393 * @param string $slug Slug to check. 394 * @param string $table_name Optional. Name of the table to check 395 * against. Default: $bp->groups->table_name. 396 * @return string|null ID of the group, if one is found, else null. 397 */ 398 public static function group_exists( $slug, $table_name = false ) { 399 global $wpdb; 400 401 if ( empty( $table_name ) ) 402 $table_name = buddypress()->groups->table_name; 403 404 if ( empty( $slug ) ) 405 return false; 406 407 return $wpdb->get_var( $wpdb->prepare( "SELECT id FROM {$table_name} WHERE slug = %s", strtolower( $slug ) ) ); 408 } 409 410 /** 411 * Get the ID of a group by the group's slug. 412 * 413 * Alias of {@link BP_Groups_Group::group_exists()}. 414 * 415 * @param string $slug See {@link BP_Groups_Group::group_exists()}. 416 * @return string|null See {@link BP_Groups_Group::group_exists()}. 417 */ 418 public static function get_id_from_slug( $slug ) { 419 return BP_Groups_Group::group_exists( $slug ); 420 } 421 422 /** 423 * Get IDs of users with outstanding invites to a given group from a specified user. 424 * 425 * @param int $user_id ID of the inviting user. 426 * @param int $group_id ID of the group. 427 * @return array IDs of users who have been invited to the group by the 428 * user but have not yet accepted. 429 */ 430 public static function get_invites( $user_id, $group_id ) { 431 global $wpdb; 432 433 $bp = buddypress(); 434 435 return $wpdb->get_col( $wpdb->prepare( "SELECT user_id FROM {$bp->groups->table_name_members} WHERE group_id = %d and is_confirmed = 0 AND inviter_id = %d", $group_id, $user_id ) ); 436 } 437 438 /** 439 * Get a list of a user's groups, filtered by a search string. 440 * 441 * @param string $filter Search term. Matches against 'name' and 442 * 'description' fields. 443 * @param int $user_id ID of the user whose groups are being searched. 444 * Default: the displayed user. 445 * @param mixed $order Not used. 446 * @param int $limit Optional. The max number of results to return. 447 * Default: null (no limit). 448 * @param int $page Optional. The page offset of results to return. 449 * Default: null (no limit). 450 * @return array { 451 * @type array $groups Array of matched and paginated group objects. 452 * @type int $total Total count of groups matching the query. 453 * } 454 */ 455 public static function filter_user_groups( $filter, $user_id = 0, $order = false, $limit = null, $page = null ) { 456 global $wpdb; 457 458 if ( empty( $user_id ) ) 459 $user_id = bp_displayed_user_id(); 460 461 $search_terms_like = bp_esc_like( $filter ) . '%'; 462 463 $pag_sql = $order_sql = $hidden_sql = ''; 464 465 if ( !empty( $limit ) && !empty( $page ) ) 466 $pag_sql = $wpdb->prepare( " LIMIT %d, %d", intval( ( $page - 1 ) * $limit), intval( $limit ) ); 467 468 // Get all the group ids for the current user's groups. 469 $gids = BP_Groups_Member::get_group_ids( $user_id ); 470 471 if ( empty( $gids['groups'] ) ) 472 return false; 473 474 $bp = buddypress(); 475 476 $gids = esc_sql( implode( ',', wp_parse_id_list( $gids['groups'] ) ) ); 477 478 $paged_groups = $wpdb->get_results( $wpdb->prepare( "SELECT id as group_id FROM {$bp->groups->table_name} WHERE ( name LIKE %s OR description LIKE %s ) AND id IN ({$gids}) {$pag_sql}", $search_terms_like, $search_terms_like ) ); 479 $total_groups = $wpdb->get_var( $wpdb->prepare( "SELECT COUNT(id) FROM {$bp->groups->table_name} WHERE ( name LIKE %s OR description LIKE %s ) AND id IN ({$gids})", $search_terms_like, $search_terms_like ) ); 480 481 return array( 'groups' => $paged_groups, 'total' => $total_groups ); 482 } 483 484 /** 485 * Get a list of groups, filtered by a search string. 486 * 487 * @param string $filter Search term. Matches against 'name' and 488 * 'description' fields. 489 * @param int $limit Optional. The max number of results to return. 490 * Default: null (no limit). 491 * @param int $page Optional. The page offset of results to return. 492 * Default: null (no limit). 493 * @param string $sort_by Column to sort by. Default: false (default 494 * sort). 495 * @param string $order ASC or DESC. Default: false (default sort). 496 * @return array { 497 * @type array $groups Array of matched and paginated group objects. 498 * @type int $total Total count of groups matching the query. 499 * } 500 */ 501 public static function search_groups( $filter, $limit = null, $page = null, $sort_by = false, $order = false ) { 502 global $wpdb; 503 504 $search_terms_like = '%' . bp_esc_like( $filter ) . '%'; 505 506 $pag_sql = $order_sql = $hidden_sql = ''; 507 508 if ( !empty( $limit ) && !empty( $page ) ) 509 $pag_sql = $wpdb->prepare( " LIMIT %d, %d", intval( ( $page - 1 ) * $limit), intval( $limit ) ); 510 511 if ( !empty( $sort_by ) && !empty( $order ) ) { 512 $sort_by = esc_sql( $sort_by ); 513 $order = esc_sql( $order ); 514 $order_sql = "ORDER BY {$sort_by} {$order}"; 515 } 516 517 if ( !bp_current_user_can( 'bp_moderate' ) ) 518 $hidden_sql = "AND status != 'hidden'"; 519 520 $bp = buddypress(); 521 522 $paged_groups = $wpdb->get_results( $wpdb->prepare( "SELECT id as group_id FROM {$bp->groups->table_name} WHERE ( name LIKE %s OR description LIKE %s ) {$hidden_sql} {$order_sql} {$pag_sql}", $search_terms_like, $search_terms_like ) ); 523 $total_groups = $wpdb->get_var( $wpdb->prepare( "SELECT COUNT(id) FROM {$bp->groups->table_name} WHERE ( name LIKE %s OR description LIKE %s ) {$hidden_sql}", $search_terms_like, $search_terms_like ) ); 524 525 return array( 'groups' => $paged_groups, 'total' => $total_groups ); 526 } 527 528 /** 529 * Check for the existence of a slug. 530 * 531 * @param string $slug Slug to check. 532 * @return string|null The slug, if found. Otherwise null. 533 */ 534 public static function check_slug( $slug ) { 535 global $wpdb; 536 537 $bp = buddypress(); 538 539 return $wpdb->get_var( $wpdb->prepare( "SELECT slug FROM {$bp->groups->table_name} WHERE slug = %s", $slug ) ); 540 } 541 542 /** 543 * Get the slug for a given group ID. 544 * 545 * @param int $group_id ID of the group. 546 * @return string|null The slug, if found. Otherwise null. 547 */ 548 public static function get_slug( $group_id ) { 549 global $wpdb; 550 551 $bp = buddypress(); 552 553 return $wpdb->get_var( $wpdb->prepare( "SELECT slug FROM {$bp->groups->table_name} WHERE id = %d", $group_id ) ); 554 } 555 556 /** 557 * Check whether a given group has any members. 558 * 559 * @param int $group_id ID of the group. 560 * @return bool True if the group has members, otherwise false. 561 */ 562 public static function has_members( $group_id ) { 563 global $wpdb; 564 565 $bp = buddypress(); 566 567 $members = $wpdb->get_var( $wpdb->prepare( "SELECT COUNT(id) FROM {$bp->groups->table_name_members} WHERE group_id = %d", $group_id ) ); 568 569 if ( empty( $members ) ) 570 return false; 571 572 return true; 573 } 574 575 /** 576 * Check whether a group has outstanding membership requests. 577 * 578 * @param int $group_id ID of the group. 579 * @return int|null The number of outstanding requests, or null if 580 * none are found. 581 */ 582 public static function has_membership_requests( $group_id ) { 583 global $wpdb; 584 585 $bp = buddypress(); 586 587 return $wpdb->get_var( $wpdb->prepare( "SELECT COUNT(id) FROM {$bp->groups->table_name_members} WHERE group_id = %d AND is_confirmed = 0", $group_id ) ); 588 } 589 590 /** 591 * Get outstanding membership requests for a group. 592 * 593 * @param int $group_id ID of the group. 594 * @param int $limit Optional. Max number of results to return. 595 * Default: null (no limit). 596 * @param int $page Optional. Page offset of results returned. Default: 597 * null (no limit). 598 * @return array { 599 * @type array $requests The requested page of located requests. 600 * @type int $total Total number of requests outstanding for the 601 * group. 602 * } 603 */ 604 public static function get_membership_requests( $group_id, $limit = null, $page = null ) { 605 global $wpdb; 606 607 if ( !empty( $limit ) && !empty( $page ) ) { 608 $pag_sql = $wpdb->prepare( " LIMIT %d, %d", intval( ( $page - 1 ) * $limit), intval( $limit ) ); 609 } 610 611 $bp = buddypress(); 612 613 $paged_requests = $wpdb->get_results( $wpdb->prepare( "SELECT * FROM {$bp->groups->table_name_members} WHERE group_id = %d AND is_confirmed = 0 AND inviter_id = 0{$pag_sql}", $group_id ) ); 614 $total_requests = $wpdb->get_var( $wpdb->prepare( "SELECT COUNT(id) FROM {$bp->groups->table_name_members} WHERE group_id = %d AND is_confirmed = 0 AND inviter_id = 0", $group_id ) ); 615 616 return array( 'requests' => $paged_requests, 'total' => $total_requests ); 617 } 618 619 /** 620 * Query for groups. 621 * 622 * @see WP_Meta_Query::queries for a description of the 'meta_query' 623 * parameter format. 624 * 625 * @param array { 626 * Array of parameters. All items are optional. 627 * @type string $type Optional. Shorthand for certain orderby/ 628 * order combinations. 'newest', 'active', 'popular', 629 * 'alphabetical', 'random'. When present, will override 630 * orderby and order params. Default: null. 631 * @type string $orderby Optional. Property to sort by. 632 * 'date_created', 'last_activity', 'total_member_count', 633 * 'name', 'random'. Default: 'date_created'. 634 * @type string $order Optional. Sort order. 'ASC' or 'DESC'. 635 * Default: 'DESC'. 636 * @type int $per_page Optional. Number of items to return per page 637 * of results. Default: null (no limit). 638 * @type int $page Optional. Page offset of results to return. 639 * Default: null (no limit). 640 * @type int $user_id Optional. If provided, results will be limited 641 * to groups of which the specified user is a member. Default: 642 * null. 643 * @type string $search_terms Optional. If provided, only groups 644 * whose names or descriptions match the search terms will be 645 * returned. Default: false. 646 * @type array $meta_query Optional. An array of meta_query 647 * conditions. See {@link WP_Meta_Query::queries} for 648 * description. 649 * @type array|string Optional. Array or comma-separated list of 650 * group IDs. Results will be limited to groups within the 651 * list. Default: false. 652 * @type bool $populate_extras Whether to fetch additional 653 * information (such as member count) about groups. Default: 654 * true. 655 * @type array|string $exclude Optional. Array or comma-separated 656 * list of group IDs. Results will exclude the listed groups. 657 * Default: false. 658 * @type bool $update_meta_cache Whether to pre-fetch groupmeta for 659 * the returned groups. Default: true. 660 * @type bool $show_hidden Whether to include hidden groups in 661 * results. Default: false. 662 * } 663 * @return array { 664 * @type array $groups Array of group objects returned by the 665 * paginated query. 666 * @type int $total Total count of all groups matching non- 667 * paginated query params. 668 * } 669 */ 670 public static function get( $args = array() ) { 671 global $wpdb; 672 673 // Backward compatibility with old method of passing arguments 674 if ( ! is_array( $args ) || func_num_args() > 1 ) { 675 _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__ ) ); 676 677 $old_args_keys = array( 678 0 => 'type', 679 1 => 'per_page', 680 2 => 'page', 681 3 => 'user_id', 682 4 => 'search_terms', 683 5 => 'include', 684 6 => 'populate_extras', 685 7 => 'exclude', 686 8 => 'show_hidden', 687 ); 688 689 $func_args = func_get_args(); 690 $args = bp_core_parse_args_array( $old_args_keys, $func_args ); 691 } 692 693 $defaults = array( 694 'type' => null, 695 'orderby' => 'date_created', 696 'order' => 'DESC', 697 'per_page' => null, 698 'page' => null, 699 'user_id' => 0, 700 'search_terms' => false, 701 'meta_query' => false, 702 'include' => false, 703 'populate_extras' => true, 704 'update_meta_cache' => true, 705 'exclude' => false, 706 'show_hidden' => false, 707 ); 708 709 $r = wp_parse_args( $args, $defaults ); 710 711 $bp = buddypress(); 712 713 $sql = array(); 714 $total_sql = array(); 715 716 $sql['select'] = "SELECT DISTINCT g.id, g.*, gm1.meta_value AS total_member_count, gm2.meta_value AS last_activity"; 717 $sql['from'] = " FROM {$bp->groups->table_name_groupmeta} gm1, {$bp->groups->table_name_groupmeta} gm2,"; 718 719 if ( ! empty( $r['user_id'] ) ) { 720 $sql['members_from'] = " {$bp->groups->table_name_members} m,"; 721 } 722 723 $sql['group_from'] = " {$bp->groups->table_name} g WHERE"; 724 725 if ( ! empty( $r['user_id'] ) ) { 726 $sql['user_where'] = " g.id = m.group_id AND"; 727 } 728 729 $sql['where'] = " g.id = gm1.group_id AND g.id = gm2.group_id AND gm2.meta_key = 'last_activity' AND gm1.meta_key = 'total_member_count'"; 730 731 if ( empty( $r['show_hidden'] ) ) { 732 $sql['hidden'] = " AND g.status != 'hidden'"; 733 } 734 735 if ( ! empty( $r['search_terms'] ) ) { 736 $search_terms_like = '%' . bp_esc_like( $r['search_terms'] ) . '%'; 737 $sql['search'] = $wpdb->prepare( " AND ( g.name LIKE %s OR g.description LIKE %s )", $search_terms_like, $search_terms_like ); 738 } 739 740 $meta_query_sql = self::get_meta_query_sql( $r['meta_query'] ); 741 742 if ( ! empty( $meta_query_sql['join'] ) ) { 743 $sql['from'] .= $meta_query_sql['join']; 744 } 745 746 if ( ! empty( $meta_query_sql['where'] ) ) { 747 $sql['meta'] = $meta_query_sql['where']; 748 } 749 750 if ( ! empty( $r['user_id'] ) ) { 751 $sql['user'] = $wpdb->prepare( " AND m.user_id = %d AND m.is_confirmed = 1 AND m.is_banned = 0", $r['user_id'] ); 752 } 753 754 if ( ! empty( $r['include'] ) ) { 755 $include = implode( ',', wp_parse_id_list( $r['include'] ) ); 756 $sql['include'] = " AND g.id IN ({$include})"; 757 } 758 759 if ( ! empty( $r['exclude'] ) ) { 760 $exclude = implode( ',', wp_parse_id_list( $r['exclude'] ) ); 761 $sql['exclude'] = " AND g.id NOT IN ({$exclude})"; 762 } 763 764 /** Order/orderby ********************************************/ 765 766 $order = $r['order']; 767 $orderby = $r['orderby']; 768 769 // If a 'type' parameter was passed, parse it and overwrite 770 // 'order' and 'orderby' params passed to the function 771 if ( ! empty( $r['type'] ) ) { 772 $order_orderby = apply_filters( 'bp_groups_get_orderby', self::convert_type_to_order_orderby( $r['type'] ), $r['type'] ); 773 774 // If an invalid type is passed, $order_orderby will be 775 // an array with empty values. In this case, we stick 776 // with the default values of $order and $orderby 777 if ( ! empty( $order_orderby['order'] ) ) { 778 $order = $order_orderby['order']; 779 } 780 781 if ( ! empty( $order_orderby['orderby'] ) ) { 782 $orderby = $order_orderby['orderby']; 783 } 784 } 785 786 // Sanitize 'order' 787 $order = bp_esc_sql_order( $order ); 788 789 // Convert 'orderby' into the proper ORDER BY term 790 $orderby = apply_filters( 'bp_groups_get_orderby_converted_by_term', self::convert_orderby_to_order_by_term( $orderby ), $orderby, $r['type'] ); 791 792 // Random order is a special case 793 if ( 'rand()' === $orderby ) { 794 $sql[] = "ORDER BY rand()"; 795 } else { 796 $sql[] = "ORDER BY {$orderby} {$order}"; 797 } 798 799 if ( ! empty( $r['per_page'] ) && ! empty( $r['page'] ) && $r['per_page'] != -1 ) { 800 $sql['pagination'] = $wpdb->prepare( "LIMIT %d, %d", intval( ( $r['page'] - 1 ) * $r['per_page']), intval( $r['per_page'] ) ); 801 } 802 803 // Get paginated results 804 $paged_groups_sql = apply_filters( 'bp_groups_get_paged_groups_sql', join( ' ', (array) $sql ), $sql, $r ); 805 $paged_groups = $wpdb->get_results( $paged_groups_sql ); 806 807 $total_sql['select'] = "SELECT COUNT(DISTINCT g.id) FROM {$bp->groups->table_name} g, {$bp->groups->table_name_groupmeta} gm"; 808 809 if ( ! empty( $r['user_id'] ) ) { 810 $total_sql['select'] .= ", {$bp->groups->table_name_members} m"; 811 } 812 813 if ( ! empty( $sql['hidden'] ) ) { 814 $total_sql['where'][] = "g.status != 'hidden'"; 815 } 816 817 if ( ! empty( $sql['search'] ) ) { 818 $total_sql['where'][] = $wpdb->prepare( "( g.name LIKE %s OR g.description LIKE %s )", $search_terms_like, $search_terms_like ); 819 } 820 821 if ( ! empty( $r['user_id'] ) ) { 822 $total_sql['where'][] = $wpdb->prepare( "m.group_id = g.id AND m.user_id = %d AND m.is_confirmed = 1 AND m.is_banned = 0", $r['user_id'] ); 823 } 824 825 // Temporary implementation of meta_query for total count 826 // See #5099 827 if ( ! empty( $meta_query_sql['where'] ) ) { 828 // Join the groupmeta table 829 $total_sql['select'] .= ", ". substr( $meta_query_sql['join'], 0, -2 ); 830 831 // Modify the meta_query clause from paged_sql for our syntax 832 $meta_query_clause = preg_replace( '/^\s*AND/', '', $meta_query_sql['where'] ); 833 $total_sql['where'][] = $meta_query_clause; 834 } 835 836 // Already escaped in the paginated results block 837 if ( ! empty( $include ) ) { 838 $total_sql['where'][] = "g.id IN ({$include})"; 839 } 840 841 // Already escaped in the paginated results block 842 if ( ! empty( $exclude ) ) { 843 $total_sql['where'][] = "g.id NOT IN ({$exclude})"; 844 } 845 846 $total_sql['where'][] = "g.id = gm.group_id"; 847 $total_sql['where'][] = "gm.meta_key = 'last_activity'"; 848 849 $t_sql = $total_sql['select']; 850 851 if ( ! empty( $total_sql['where'] ) ) { 852 $t_sql .= " WHERE " . join( ' AND ', (array) $total_sql['where'] ); 853 } 854 855 // Get total group results 856 $total_groups_sql = apply_filters( 'bp_groups_get_total_groups_sql', $t_sql, $total_sql, $r ); 857 $total_groups = $wpdb->get_var( $total_groups_sql ); 858 859 $group_ids = array(); 860 foreach ( (array) $paged_groups as $group ) { 861 $group_ids[] = $group->id; 862 } 863 864 // Populate some extra information instead of querying each time in the loop 865 if ( !empty( $r['populate_extras'] ) ) { 866 $paged_groups = BP_Groups_Group::get_group_extras( $paged_groups, $group_ids, $r['type'] ); 867 } 868 869 // Grab all groupmeta 870 if ( ! empty( $r['update_meta_cache'] ) ) { 871 bp_groups_update_meta_cache( $group_ids ); 872 } 873 874 unset( $sql, $total_sql ); 875 876 return array( 'groups' => $paged_groups, 'total' => $total_groups ); 877 } 878 879 /** 880 * Get the SQL for the 'meta_query' param in BP_Activity_Activity::get() 881 * 882 * We use WP_Meta_Query to do the heavy lifting of parsing the 883 * meta_query array and creating the necessary SQL clauses. However, 884 * since BP_Activity_Activity::get() builds its SQL differently than 885 * WP_Query, we have to alter the return value (stripping the leading 886 * AND keyword from the 'where' clause). 887 * 888 * @since BuddyPress (1.8.0) 889 * @access protected 890 * 891 * @param array $meta_query An array of meta_query filters. See the 892 * documentation for {@link WP_Meta_Query} for details. 893 * @return array $sql_array 'join' and 'where' clauses. 894 */ 895 protected static function get_meta_query_sql( $meta_query = array() ) { 896 global $wpdb; 897 898 $sql_array = array( 899 'join' => '', 900 'where' => '', 901 ); 902 903 if ( ! empty( $meta_query ) ) { 904 $groups_meta_query = new WP_Meta_Query( $meta_query ); 905 906 // WP_Meta_Query expects the table name at 907 // $wpdb->group 908 $wpdb->groupmeta = buddypress()->groups->table_name_groupmeta; 909 910 $meta_sql = $groups_meta_query->get_sql( 'group', 'g', 'id' ); 911 912 // BP_Groups_Group::get uses the comma syntax for table 913 // joins, which means that we have to do some regex to 914 // convert the INNER JOIN and move the ON clause to a 915 // WHERE condition 916 // 917 // @todo It may be better in the long run to refactor 918 // the more general query syntax to accord better with 919 // BP/WP convention 920 preg_match_all( '/JOIN (.+?) ON/', $meta_sql['join'], $matches_a ); 921 preg_match_all( '/ON \((.+?)\)/', $meta_sql['join'], $matches_b ); 922 923 if ( ! empty( $matches_a[1] ) && ! empty( $matches_b[1] ) ) { 924 $sql_array['join'] = implode( ',', $matches_a[1] ) . ', '; 925 $sql_array['where'] = $meta_sql['where'] . ' AND ' . implode( ' AND ', $matches_b[1] ); 926 } 927 } 928 929 return $sql_array; 930 } 931 932 /** 933 * Convert the 'type' parameter to 'order' and 'orderby'. 934 * 935 * @since BuddyPress (1.8.0) 936 * @access protected 937 * 938 * @param string $type The 'type' shorthand param. 939 * @return array { 940 * @type string $order SQL-friendly order string. 941 * @type string $orderby SQL-friendly orderby column name. 942 * } 943 */ 944 protected static function convert_type_to_order_orderby( $type = '' ) { 945 $order = $orderby = ''; 946 947 switch ( $type ) { 948 case 'newest' : 949 $order = 'DESC'; 950 $orderby = 'date_created'; 951 break; 952 953 case 'active' : 954 $order = 'DESC'; 955 $orderby = 'last_activity'; 956 break; 957 958 case 'popular' : 959 $order = 'DESC'; 960 $orderby = 'total_member_count'; 961 break; 962 963 case 'alphabetical' : 964 $order = 'ASC'; 965 $orderby = 'name'; 966 break; 967 968 case 'random' : 969 $order = ''; 970 $orderby = 'random'; 971 break; 972 } 973 974 return array( 'order' => $order, 'orderby' => $orderby ); 975 } 976 977 /** 978 * Convert the 'orderby' param into a proper SQL term/column. 979 * 980 * @since BuddyPress (1.8.0) 981 * @access protected 982 * 983 * @param string $orderby Orderby term as passed to get(). 984 * @return string $order_by_term SQL-friendly orderby term. 985 */ 986 protected static function convert_orderby_to_order_by_term( $orderby ) { 987 $order_by_term = ''; 988 989 switch ( $orderby ) { 990 case 'date_created' : 991 default : 992 $order_by_term = 'g.date_created'; 993 break; 994 995 case 'last_activity' : 996 $order_by_term = 'last_activity'; 997 break; 998 999 case 'total_member_count' : 1000 $order_by_term = 'CONVERT(gm1.meta_value, SIGNED)'; 1001 break; 1002 1003 case 'name' : 1004 $order_by_term = 'g.name'; 1005 break; 1006 1007 case 'random' : 1008 $order_by_term = 'rand()'; 1009 break; 1010 } 1011 1012 return $order_by_term; 1013 } 1014 1015 /** 1016 * Get a list of groups, sorted by those that have the most legacy forum topics. 1017 * 1018 * @param int $limit Optional. The max number of results to return. 1019 * Default: null (no limit). 1020 * @param int $page Optional. The page offset of results to return. 1021 * Default: null (no limit). 1022 * @param int $user_id Optional. If present, groups will be limited to 1023 * those of which the specified user is a member. 1024 * @param string $search_terms Optional. Limit groups to those whose 1025 * name or description field contain the search string. 1026 * @param bool $populate_extras Optional. Whether to fetch extra 1027 * information about the groups. Default: true. 1028 * @param string|array Optional. Array or comma-separated list of group 1029 * IDs to exclude from results. 1030 * @return array { 1031 * @type array $groups Array of group objects returned by the 1032 * paginated query. 1033 * @type int $total Total count of all groups matching non- 1034 * paginated query params. 1035 * } 1036 */ 1037 public static function get_by_most_forum_topics( $limit = null, $page = null, $user_id = 0, $search_terms = false, $populate_extras = true, $exclude = false ) { 1038 global $wpdb, $bbdb; 1039 1040 if ( empty( $bbdb ) ) 1041 do_action( 'bbpress_init' ); 1042 1043 if ( !empty( $limit ) && !empty( $page ) ) { 1044 $pag_sql = $wpdb->prepare( " LIMIT %d, %d", intval( ( $page - 1 ) * $limit), intval( $limit ) ); 1045 } 1046 1047 if ( !is_user_logged_in() || ( !bp_current_user_can( 'bp_moderate' ) && ( $user_id != bp_loggedin_user_id() ) ) ) 1048 $hidden_sql = " AND g.status != 'hidden'"; 1049 1050 if ( !empty( $search_terms ) ) { 1051 $search_terms_like = '%' . bp_esc_like( $search_terms ) . '%'; 1052 $search_sql = $wpdb->prepare( ' AND ( g.name LIKE %s OR g.description LIKE %s ) ', $search_terms_like, $search_terms_like ); 1053 } 1054 1055 if ( !empty( $exclude ) ) { 1056 $exclude = implode( ',', wp_parse_id_list( $exclude ) ); 1057 $exclude_sql = " AND g.id NOT IN ({$exclude})"; 1058 } 1059 1060 $bp = buddypress(); 1061 1062 if ( !empty( $user_id ) ) { 1063 $user_id = absint( esc_sql( $user_id ) ); 1064 $paged_groups = $wpdb->get_results( "SELECT DISTINCT g.*, gm1.meta_value as total_member_count, gm2.meta_value as last_activity FROM {$bp->groups->table_name_groupmeta} gm1, {$bp->groups->table_name_groupmeta} gm2, {$bp->groups->table_name_groupmeta} gm3, {$bp->groups->table_name_members} m, {$bbdb->forums} f, {$bp->groups->table_name} g WHERE g.id = m.group_id AND g.id = gm1.group_id AND g.id = gm2.group_id AND g.id = gm3.group_id AND gm2.meta_key = 'last_activity' AND gm1.meta_key = 'total_member_count' AND (gm3.meta_key = 'forum_id' AND gm3.meta_value = f.forum_id) AND f.topics > 0 {$hidden_sql} {$search_sql} AND m.user_id = {$user_id} AND m.is_confirmed = 1 AND m.is_banned = 0 {$exclude_sql} ORDER BY f.topics DESC {$pag_sql}" ); 1065 $total_groups = $wpdb->get_var( "SELECT COUNT(DISTINCT g.id) FROM {$bp->groups->table_name_groupmeta} gm1, {$bp->groups->table_name_groupmeta} gm2, {$bp->groups->table_name_groupmeta} gm3, {$bbdb->forums} f, {$bp->groups->table_name} g WHERE g.id = gm1.group_id AND g.id = gm2.group_id AND g.id = gm3.group_id AND gm2.meta_key = 'last_activity' AND gm1.meta_key = 'total_member_count' AND (gm3.meta_key = 'forum_id' AND gm3.meta_value = f.forum_id) AND f.topics > 0 {$hidden_sql} {$search_sql} AND m.user_id = {$user_id} AND m.is_confirmed = 1 AND m.is_banned = 0 {$exclude_sql}" ); 1066 } else { 1067 $paged_groups = $wpdb->get_results( "SELECT DISTINCT g.*, gm1.meta_value as total_member_count, gm2.meta_value as last_activity FROM {$bp->groups->table_name_groupmeta} gm1, {$bp->groups->table_name_groupmeta} gm2, {$bp->groups->table_name_groupmeta} gm3, {$bbdb->forums} f, {$bp->groups->table_name} g WHERE g.id = gm1.group_id AND g.id = gm2.group_id AND g.id = gm3.group_id AND gm2.meta_key = 'last_activity' AND gm1.meta_key = 'total_member_count' AND (gm3.meta_key = 'forum_id' AND gm3.meta_value = f.forum_id) AND f.topics > 0 {$hidden_sql} {$search_sql} {$exclude_sql} ORDER BY f.topics DESC {$pag_sql}" ); 1068 $total_groups = $wpdb->get_var( "SELECT COUNT(DISTINCT g.id) FROM {$bp->groups->table_name_groupmeta} gm1, {$bp->groups->table_name_groupmeta} gm2, {$bp->groups->table_name_groupmeta} gm3, {$bbdb->forums} f, {$bp->groups->table_name} g WHERE g.id = gm1.group_id AND g.id = gm2.group_id AND g.id = gm3.group_id AND gm2.meta_key = 'last_activity' AND gm1.meta_key = 'total_member_count' AND (gm3.meta_key = 'forum_id' AND gm3.meta_value = f.forum_id) AND f.topics > 0 {$hidden_sql} {$search_sql} {$exclude_sql}" ); 1069 } 1070 1071 if ( !empty( $populate_extras ) ) { 1072 foreach ( (array) $paged_groups as $group ) { 1073 $group_ids[] = $group->id; 1074 } 1075 $paged_groups = BP_Groups_Group::get_group_extras( $paged_groups, $group_ids, 'newest' ); 1076 } 1077 1078 return array( 'groups' => $paged_groups, 'total' => $total_groups ); 1079 } 1080 1081 /** 1082 * Get a list of groups, sorted by those that have the most legacy forum posts. 1083 * 1084 * @param int $limit Optional. The max number of results to return. 1085 * Default: null (no limit). 1086 * @param int $page Optional. The page offset of results to return. 1087 * Default: null (no limit). 1088 * @param int $user_id Optional. If present, groups will be limited to 1089 * those of which the specified user is a member. 1090 * @param string $search_terms Optional. Limit groups to those whose 1091 * name or description field contain the search string. 1092 * @param bool $populate_extras Optional. Whether to fetch extra 1093 * information about the groups. Default: true. 1094 * @param string|array Optional. Array or comma-separated list of group 1095 * IDs to exclude from results. 1096 * @return array { 1097 * @type array $groups Array of group objects returned by the 1098 * paginated query. 1099 * @type int $total Total count of all groups matching non- 1100 * paginated query params. 1101 * } 1102 */ 1103 public static function get_by_most_forum_posts( $limit = null, $page = null, $search_terms = false, $populate_extras = true, $exclude = false ) { 1104 global $wpdb, $bbdb; 1105 1106 if ( empty( $bbdb ) ) 1107 do_action( 'bbpress_init' ); 1108 1109 if ( !empty( $limit ) && !empty( $page ) ) { 1110 $pag_sql = $wpdb->prepare( " LIMIT %d, %d", intval( ( $page - 1 ) * $limit), intval( $limit ) ); 1111 } 1112 1113 if ( !is_user_logged_in() || ( !bp_current_user_can( 'bp_moderate' ) && ( $user_id != bp_loggedin_user_id() ) ) ) 1114 $hidden_sql = " AND g.status != 'hidden'"; 1115 1116 if ( !empty( $search_terms ) ) { 1117 $search_terms_like = '%' . bp_esc_like( $search_terms ) . '%'; 1118 $search_sql = $wpdb->prepare( ' AND ( g.name LIKE %s OR g.description LIKE %s ) ', $search_terms_like, $search_terms_like ); 1119 } 1120 1121 if ( !empty( $exclude ) ) { 1122 $exclude = implode( ',', wp_parse_id_list( $exclude ) ); 1123 $exclude_sql = " AND g.id NOT IN ({$exclude})"; 1124 } 1125 1126 $bp = buddypress(); 1127 1128 if ( !empty( $user_id ) ) { 1129 $user_id = esc_sql( $user_id ); 1130 $paged_groups = $wpdb->get_results( "SELECT DISTINCT g.*, gm1.meta_value as total_member_count, gm2.meta_value as last_activity FROM {$bp->groups->table_name_groupmeta} gm1, {$bp->groups->table_name_groupmeta} gm2, {$bp->groups->table_name_groupmeta} gm3, {$bp->groups->table_name_members} m, {$bbdb->forums} f, {$bp->groups->table_name} g WHERE g.id = m.group_id AND g.id = gm1.group_id AND g.id = gm2.group_id AND g.id = gm3.group_id AND gm2.meta_key = 'last_activity' AND gm1.meta_key = 'total_member_count' AND (gm3.meta_key = 'forum_id' AND gm3.meta_value = f.forum_id) {$hidden_sql} {$search_sql} AND m.user_id = {$user_id} AND m.is_confirmed = 1 AND m.is_banned = 0 {$exclude_sql} ORDER BY f.posts ASC {$pag_sql}" ); 1131 $total_groups = $wpdb->get_results( "SELECT COUNT(DISTINCT g.id) FROM {$bp->groups->table_name_groupmeta} gm1, {$bp->groups->table_name_groupmeta} gm2, {$bp->groups->table_name_groupmeta} gm3, {$bp->groups->table_name_members} m, {$bbdb->forums} f, {$bp->groups->table_name} g WHERE g.id = m.group_id AND g.id = gm1.group_id AND g.id = gm2.group_id AND g.id = gm3.group_id AND gm2.meta_key = 'last_activity' AND gm1.meta_key = 'total_member_count' AND (gm3.meta_key = 'forum_id' AND gm3.meta_value = f.forum_id) AND f.posts > 0 {$hidden_sql} {$search_sql} AND m.user_id = {$user_id} AND m.is_confirmed = 1 AND m.is_banned = 0 {$exclude_sql} " ); 1132 } else { 1133 $paged_groups = $wpdb->get_results( "SELECT DISTINCT g.*, gm1.meta_value as total_member_count, gm2.meta_value as last_activity FROM {$bp->groups->table_name_groupmeta} gm1, {$bp->groups->table_name_groupmeta} gm2, {$bp->groups->table_name_groupmeta} gm3, {$bbdb->forums} f, {$bp->groups->table_name} g WHERE g.id = gm1.group_id AND g.id = gm2.group_id AND g.id = gm3.group_id AND gm2.meta_key = 'last_activity' AND gm1.meta_key = 'total_member_count' AND (gm3.meta_key = 'forum_id' AND gm3.meta_value = f.forum_id) AND f.posts > 0 {$hidden_sql} {$search_sql} {$exclude_sql} ORDER BY f.posts ASC {$pag_sql}" ); 1134 $total_groups = $wpdb->get_var( "SELECT COUNT(DISTINCT g.id) FROM {$bp->groups->table_name_groupmeta} gm1, {$bp->groups->table_name_groupmeta} gm2, {$bp->groups->table_name_groupmeta} gm3, {$bbdb->forums} f, {$bp->groups->table_name} g WHERE g.id = gm1.group_id AND g.id = gm2.group_id AND g.id = gm3.group_id AND gm2.meta_key = 'last_activity' AND gm1.meta_key = 'total_member_count' AND (gm3.meta_key = 'forum_id' AND gm3.meta_value = f.forum_id) {$hidden_sql} {$search_sql} {$exclude_sql}" ); 1135 } 1136 1137 if ( !empty( $populate_extras ) ) { 1138 foreach ( (array) $paged_groups as $group ) { 1139 $group_ids[] = $group->id; 1140 } 1141 $paged_groups = BP_Groups_Group::get_group_extras( $paged_groups, $group_ids, 'newest' ); 1142 } 1143 1144 return array( 'groups' => $paged_groups, 'total' => $total_groups ); 1145 } 1146 1147 /** 1148 * Get a list of groups whose names start with a given letter. 1149 * 1150 * @param string $letter The letter. 1151 * @param int $limit Optional. The max number of results to return. 1152 * Default: null (no limit). 1153 * @param int $page Optional. The page offset of results to return. 1154 * Default: null (no limit). 1155 * @param bool $populate_extras Optional. Whether to fetch extra 1156 * information about the groups. Default: true. 1157 * @param string|array Optional. Array or comma-separated list of group 1158 * IDs to exclude from results. 1159 * @return array { 1160 * @type array $groups Array of group objects returned by the 1161 * paginated query. 1162 * @type int $total Total count of all groups matching non- 1163 * paginated query params. 1164 * } 1165 */ 1166 public static function get_by_letter( $letter, $limit = null, $page = null, $populate_extras = true, $exclude = false ) { 1167 global $wpdb; 1168 1169 $pag_sql = $hidden_sql = $exclude_sql = ''; 1170 1171 // Multibyte compliance 1172 if ( function_exists( 'mb_strlen' ) ) { 1173 if ( mb_strlen( $letter, 'UTF-8' ) > 1 || is_numeric( $letter ) || !$letter ) { 1174 return false; 1175 } 1176 } else { 1177 if ( strlen( $letter ) > 1 || is_numeric( $letter ) || !$letter ) { 1178 return false; 1179 } 1180 } 1181 1182 $bp = buddypress(); 1183 1184 if ( !empty( $exclude ) ) { 1185 $exclude = implode( ',', wp_parse_id_list( $exclude ) ); 1186 $exclude_sql = " AND g.id NOT IN ({$exclude})"; 1187 } 1188 1189 if ( !bp_current_user_can( 'bp_moderate' ) ) 1190 $hidden_sql = " AND status != 'hidden'"; 1191 1192 $letter_like = bp_esc_like( $letter ) . '%'; 1193 1194 if ( !empty( $limit ) && !empty( $page ) ) { 1195 $pag_sql = $wpdb->prepare( " LIMIT %d, %d", intval( ( $page - 1 ) * $limit), intval( $limit ) ); 1196 } 1197 1198 $total_groups = $wpdb->get_var( $wpdb->prepare( "SELECT COUNT(DISTINCT g.id) FROM {$bp->groups->table_name_groupmeta} gm1, {$bp->groups->table_name_groupmeta} gm2, {$bp->groups->table_name} g WHERE g.id = gm1.group_id AND g.id = gm2.group_id AND gm2.meta_key = 'last_activity' AND gm1.meta_key = 'total_member_count' AND g.name LIKE %s {$hidden_sql} {$exclude_sql}", $letter_like ) ); 1199 1200 $paged_groups = $wpdb->get_results( $wpdb->prepare( "SELECT g.*, gm1.meta_value as total_member_count, gm2.meta_value as last_activity FROM {$bp->groups->table_name_groupmeta} gm1, {$bp->groups->table_name_groupmeta} gm2, {$bp->groups->table_name} g WHERE g.id = gm1.group_id AND g.id = gm2.group_id AND gm2.meta_key = 'last_activity' AND gm1.meta_key = 'total_member_count' AND g.name LIKE %s {$hidden_sql} {$exclude_sql} ORDER BY g.name ASC {$pag_sql}", $letter_like ) ); 1201 1202 if ( !empty( $populate_extras ) ) { 1203 foreach ( (array) $paged_groups as $group ) { 1204 $group_ids[] = $group->id; 1205 } 1206 $paged_groups = BP_Groups_Group::get_group_extras( $paged_groups, $group_ids, 'newest' ); 1207 } 1208 1209 return array( 'groups' => $paged_groups, 'total' => $total_groups ); 1210 } 1211 1212 /** 1213 * Get a list of random groups. 1214 * 1215 * Use BP_Groups_Group::get() with 'type' = 'random' instead. 1216 * 1217 * @param int $limit Optional. The max number of results to return. 1218 * Default: null (no limit). 1219 * @param int $page Optional. The page offset of results to return. 1220 * Default: null (no limit). 1221 * @param int $user_id Optional. If present, groups will be limited to 1222 * those of which the specified user is a member. 1223 * @param string $search_terms Optional. Limit groups to those whose 1224 * name or description field contain the search string. 1225 * @param bool $populate_extras Optional. Whether to fetch extra 1226 * information about the groups. Default: true. 1227 * @param string|array Optional. Array or comma-separated list of group 1228 * IDs to exclude from results. 1229 * @return array { 1230 * @type array $groups Array of group objects returned by the 1231 * paginated query. 1232 * @type int $total Total count of all groups matching non- 1233 * paginated query params. 1234 * } 1235 */ 1236 public static function get_random( $limit = null, $page = null, $user_id = 0, $search_terms = false, $populate_extras = true, $exclude = false ) { 1237 global $wpdb; 1238 1239 $pag_sql = $hidden_sql = $search_sql = $exclude_sql = ''; 1240 1241 if ( !empty( $limit ) && !empty( $page ) ) 1242 $pag_sql = $wpdb->prepare( " LIMIT %d, %d", intval( ( $page - 1 ) * $limit), intval( $limit ) ); 1243 1244 if ( !is_user_logged_in() || ( !bp_current_user_can( 'bp_moderate' ) && ( $user_id != bp_loggedin_user_id() ) ) ) 1245 $hidden_sql = "AND g.status != 'hidden'"; 1246 1247 if ( !empty( $search_terms ) ) { 1248 $search_terms_like = '%' . bp_esc_like( $search_terms ) . '%'; 1249 $search_sql = $wpdb->prepare( " AND ( g.name LIKE %s OR g.description LIKE %s )", $search_terms_like, $search_terms_like ); 1250 } 1251 1252 if ( !empty( $exclude ) ) { 1253 $exclude = wp_parse_id_list( $exclude ); 1254 $exclude = esc_sql( implode( ',', $exclude ) ); 1255 $exclude_sql = " AND g.id NOT IN ({$exclude})"; 1256 } 1257 1258 $bp = buddypress(); 1259 1260 if ( !empty( $user_id ) ) { 1261 $user_id = esc_sql( $user_id ); 1262 $paged_groups = $wpdb->get_results( "SELECT g.*, gm1.meta_value as total_member_count, gm2.meta_value as last_activity FROM {$bp->groups->table_name_groupmeta} gm1, {$bp->groups->table_name_groupmeta} gm2, {$bp->groups->table_name_members} m, {$bp->groups->table_name} g WHERE g.id = m.group_id AND g.id = gm1.group_id AND g.id = gm2.group_id AND gm2.meta_key = 'last_activity' AND gm1.meta_key = 'total_member_count' {$hidden_sql} {$search_sql} AND m.user_id = {$user_id} AND m.is_confirmed = 1 AND m.is_banned = 0 {$exclude_sql} ORDER BY rand() {$pag_sql}" ); 1263 $total_groups = $wpdb->get_var( "SELECT COUNT(DISTINCT m.group_id) FROM {$bp->groups->table_name_members} m LEFT JOIN {$bp->groups->table_name_groupmeta} gm ON m.group_id = gm.group_id INNER JOIN {$bp->groups->table_name} g ON m.group_id = g.id WHERE gm.meta_key = 'last_activity'{$hidden_sql} {$search_sql} AND m.user_id = {$user_id} AND m.is_confirmed = 1 AND m.is_banned = 0 {$exclude_sql}" ); 1264 } else { 1265 $paged_groups = $wpdb->get_results( "SELECT g.*, gm1.meta_value as total_member_count, gm2.meta_value as last_activity FROM {$bp->groups->table_name_groupmeta} gm1, {$bp->groups->table_name_groupmeta} gm2, {$bp->groups->table_name} g WHERE g.id = gm1.group_id AND g.id = gm2.group_id AND gm2.meta_key = 'last_activity' AND gm1.meta_key = 'total_member_count' {$hidden_sql} {$search_sql} {$exclude_sql} ORDER BY rand() {$pag_sql}" ); 1266 $total_groups = $wpdb->get_var( "SELECT COUNT(DISTINCT g.id) FROM {$bp->groups->table_name_groupmeta} gm INNER JOIN {$bp->groups->table_name} g ON gm.group_id = g.id WHERE gm.meta_key = 'last_activity'{$hidden_sql} {$search_sql} {$exclude_sql}" ); 1267 } 1268 1269 if ( !empty( $populate_extras ) ) { 1270 foreach ( (array) $paged_groups as $group ) { 1271 $group_ids[] = $group->id; 1272 } 1273 $paged_groups = BP_Groups_Group::get_group_extras( $paged_groups, $group_ids, 'newest' ); 1274 } 1275 1276 return array( 'groups' => $paged_groups, 'total' => $total_groups ); 1277 } 1278 1279 /** 1280 * Fetch extra data for a list of groups. 1281 * 1282 * This method is used throughout the class, by methods that take a 1283 * $populate_extras parameter. 1284 * 1285 * Data fetched: 1286 * 1287 * - Logged-in user's status within each group (is_member, 1288 * is_confirmed, is_pending, is_banned) 1289 * 1290 * @param array $paged_groups Array of groups. 1291 * @param string|array Array or comma-separated list of IDs matching 1292 * $paged_groups. 1293 * @param string $type Not used. 1294 * @return array $paged_groups 1295 */ 1296 public static function get_group_extras( &$paged_groups, &$group_ids, $type = false ) { 1297 global $wpdb; 1298 1299 if ( empty( $group_ids ) ) 1300 return $paged_groups; 1301 1302 $bp = buddypress(); 1303 1304 // Sanitize group IDs 1305 $group_ids = implode( ',', wp_parse_id_list( $group_ids ) ); 1306 1307 // Fetch the logged-in user's status within each group 1308 if ( is_user_logged_in() ) { 1309 $user_status_results = $wpdb->get_results( $wpdb->prepare( "SELECT group_id, is_confirmed, invite_sent FROM {$bp->groups->table_name_members} WHERE user_id = %d AND group_id IN ( {$group_ids} ) AND is_banned = 0", bp_loggedin_user_id() ) ); 1310 } else { 1311 $user_status_results = array(); 1312 } 1313 1314 // Reindex 1315 $user_status = array(); 1316 foreach ( $user_status_results as $user_status_result ) { 1317 $user_status[ $user_status_result->group_id ] = $user_status_result; 1318 } 1319 1320 for ( $i = 0, $count = count( $paged_groups ); $i < $count; ++$i ) { 1321 $is_member = $is_invited = $is_pending = '0'; 1322 $gid = $paged_groups[ $i ]->id; 1323 1324 if ( isset( $user_status[ $gid ] ) ) { 1325 1326 // is_confirmed means the user is a member 1327 if ( $user_status[ $gid ]->is_confirmed ) { 1328 $is_member = '1'; 1329 1330 // invite_sent means the user has been invited 1331 } elseif ( $user_status[ $gid ]->invite_sent ) { 1332 $is_invited = '1'; 1333 1334 // User has sent request, but has not been confirmed 1335 } else { 1336 $is_pending = '1'; 1337 } 1338 } 1339 1340 $paged_groups[ $i ]->is_member = $is_member; 1341 $paged_groups[ $i ]->is_invited = $is_invited; 1342 $paged_groups[ $i ]->is_pending = $is_pending; 1343 } 1344 1345 if ( is_user_logged_in() ) { 1346 $user_banned = $wpdb->get_col( $wpdb->prepare( "SELECT group_id FROM {$bp->groups->table_name_members} WHERE is_banned = 1 AND user_id = %d AND group_id IN ( {$group_ids} )", bp_loggedin_user_id() ) ); 1347 } else { 1348 $user_banned = array(); 1349 } 1350 1351 for ( $i = 0, $count = count( $paged_groups ); $i < $count; ++$i ) { 1352 $paged_groups[$i]->is_banned = false; 1353 1354 foreach ( (array) $user_banned as $group_id ) { 1355 if ( $group_id == $paged_groups[$i]->id ) { 1356 $paged_groups[$i]->is_banned = true; 1357 } 1358 } 1359 } 1360 1361 return $paged_groups; 1362 } 1363 1364 /** 1365 * Delete all invitations to a given group. 1366 * 1367 * @param int $group_id ID of the group whose invitations are being 1368 * deleted. 1369 * @return int|null Number of rows records deleted on success, null on 1370 * failure. 1371 */ 1372 public static function delete_all_invites( $group_id ) { 1373 global $wpdb; 1374 1375 $bp = buddypress(); 1376 1377 return $wpdb->query( $wpdb->prepare( "DELETE FROM {$bp->groups->table_name_members} WHERE group_id = %d AND invite_sent = 1", $group_id ) ); 1378 } 1379 1380 /** 1381 * Get a total group count for the site. 1382 * 1383 * Will include hidden groups in the count only if 1384 * current_user_can( 'bp_moderate' ). 1385 * 1386 * @return int Group count. 1387 */ 1388 public static function get_total_group_count() { 1389 global $wpdb; 1390 1391 $hidden_sql = ''; 1392 if ( !bp_current_user_can( 'bp_moderate' ) ) 1393 $hidden_sql = "WHERE status != 'hidden'"; 1394 1395 $bp = buddypress(); 1396 1397 return $wpdb->get_var( "SELECT COUNT(id) FROM {$bp->groups->table_name} {$hidden_sql}" ); 1398 } 1399 1400 /** 1401 * Get global count of forum topics in public groups (legacy forums). 1402 * 1403 * @param $type Optional. If 'unreplied', count will be limited to 1404 * those topics that have received no replies. 1405 * @return int Forum topic count. 1406 */ 1407 public static function get_global_forum_topic_count( $type ) { 1408 global $bbdb, $wpdb; 1409 1410 $bp = buddypress(); 1411 1412 if ( 'unreplied' == $type ) 1413 $bp->groups->filter_sql = ' AND t.topic_posts = 1'; 1414 1415 // https://buddypress.trac.wordpress.org/ticket/4306 1416 $extra_sql = apply_filters( 'get_global_forum_topic_count_extra_sql', $bp->groups->filter_sql, $type ); 1417 1418 // Make sure the $extra_sql begins with an AND 1419 if ( 'AND' != substr( trim( strtoupper( $extra_sql ) ), 0, 3 ) ) 1420 $extra_sql = ' AND ' . $extra_sql; 1421 1422 return $wpdb->get_var( "SELECT COUNT(t.topic_id) FROM {$bbdb->topics} AS t, {$bp->groups->table_name} AS g LEFT JOIN {$bp->groups->table_name_groupmeta} AS gm ON g.id = gm.group_id WHERE (gm.meta_key = 'forum_id' AND gm.meta_value = t.forum_id) AND g.status = 'public' AND t.topic_status = '0' AND t.topic_sticky != '2' {$extra_sql} " ); 1423 } 1424 1425 /** 1426 * Get the member count for a group. 1427 * 1428 * @param int $group_id Group ID. 1429 * @return int Count of confirmed members for the group. 1430 */ 1431 public static function get_total_member_count( $group_id ) { 1432 global $wpdb; 1433 1434 $bp = buddypress(); 1435 1436 return $wpdb->get_var( $wpdb->prepare( "SELECT COUNT(id) FROM {$bp->groups->table_name_members} WHERE group_id = %d AND is_confirmed = 1 AND is_banned = 0", $group_id ) ); 1437 } 1438 1439 /** 1440 * Get a total count of all topics of a given status, across groups/forums 1441 * 1442 * @since BuddyPress (1.5.0) 1443 * 1444 * @param string $status Which group type to count. 'public', 'private', 1445 * 'hidden', or 'all'. Default: 'public'. 1446 * @return int The topic count 1447 */ 1448 public static function get_global_topic_count( $status = 'public', $search_terms = false ) { 1449 global $bbdb, $wpdb; 1450 1451 switch ( $status ) { 1452 case 'all' : 1453 $status_sql = ''; 1454 break; 1455 1456 case 'hidden' : 1457 $status_sql = "AND g.status = 'hidden'"; 1458 break; 1459 1460 case 'private' : 1461 $status_sql = "AND g.status = 'private'"; 1462 break; 1463 1464 case 'public' : 1465 default : 1466 $status_sql = "AND g.status = 'public'"; 1467 break; 1468 } 1469 1470 $bp = buddypress(); 1471 1472 $sql = array(); 1473 1474 $sql['select'] = "SELECT COUNT(t.topic_id)"; 1475 $sql['from'] = "FROM {$bbdb->topics} AS t INNER JOIN {$bp->groups->table_name_groupmeta} AS gm ON t.forum_id = gm.meta_value INNER JOIN {$bp->groups->table_name} AS g ON gm.group_id = g.id"; 1476 $sql['where'] = "WHERE gm.meta_key = 'forum_id' {$status_sql} AND t.topic_status = '0' AND t.topic_sticky != '2'"; 1477 1478 if ( !empty( $search_terms ) ) { 1479 $search_terms_like = '%' . bp_esc_like( $search_terms ) . '%'; 1480 $sql['where'] .= $wpdb->prepare( " AND ( t.topic_title LIKE %s )", $search_terms_like ); 1481 } 1482 1483 return $wpdb->get_var( implode( ' ', $sql ) ); 1484 } 1485 1486 /** 1487 * Get an array containing ids for each group type. 1488 * 1489 * A bit of a kludge workaround for some issues 1490 * with bp_has_groups(). 1491 * 1492 * @since BuddyPress (1.7.0) 1493 * 1494 * @return array 1495 */ 1496 public static function get_group_type_ids() { 1497 global $wpdb; 1498 1499 $bp = buddypress(); 1500 $ids = array(); 1501 1502 $ids['all'] = $wpdb->get_col( "SELECT id FROM {$bp->groups->table_name}" ); 1503 $ids['public'] = $wpdb->get_col( "SELECT id FROM {$bp->groups->table_name} WHERE status = 'public'" ); 1504 $ids['private'] = $wpdb->get_col( "SELECT id FROM {$bp->groups->table_name} WHERE status = 'private'" ); 1505 $ids['hidden'] = $wpdb->get_col( "SELECT id FROM {$bp->groups->table_name} WHERE status = 'hidden'" ); 1506 1507 return $ids; 1508 } 1509 } 1510 1511 /** 1512 * Query for the members of a group. 1513 * 1514 * Special notes about the group members data schema: 1515 * - *Members* are entries with is_confirmed = 1 1516 * - *Pending requests* are entries with is_confirmed = 0 and inviter_id = 0 1517 * - *Pending and sent invitations* are entries with is_confirmed = 0 and 1518 * inviter_id != 0 and invite_sent = 1 1519 * - *Pending and unsent invitations* are entries with is_confirmed = 0 and 1520 * inviter_id != 0 and invite_sent = 0 1521 * - *Membership requests* are entries with is_confirmed = 0 and 1522 * inviter_id = 0 (and invite_sent = 0) 1523 * 1524 * @since BuddyPress (1.8.0) 1525 * 1526 * @param array $args { 1527 * Array of arguments. Accepts all arguments from 1528 * {@link BP_User_Query}, with the following additions: 1529 * @type int $group_id ID of the group to limit results to. 1530 * @type array $group_role Array of group roles to match ('member', 1531 * 'mod', 'admin', 'banned'). Default: array( 'member' ). 1532 * @type bool $is_confirmed Whether to limit to confirmed members. 1533 * Default: true. 1534 * @type string $type Sort order. Accepts any value supported by 1535 * {@link BP_User_Query}, in addition to 'last_joined' and 1536 * 'first_joined'. Default: 'last_joined'. 1537 * } 1538 */ 1539 class BP_Group_Member_Query extends BP_User_Query { 1540 1541 /** 1542 * Array of group member ids, cached to prevent redundant lookups. 1543 * 1544 * @since BuddyPress (1.8.1) 1545 * @access protected 1546 * @var null|array Null if not yet defined, otherwise an array of ints. 1547 */ 1548 protected $group_member_ids; 1549 1550 /** 1551 * Set up action hooks. 1552 * 1553 * @since BuddyPress (1.8.0) 1554 */ 1555 public function setup_hooks() { 1556 // Take this early opportunity to set the default 'type' param 1557 // to 'last_joined', which will ensure that BP_User_Query 1558 // trusts our order and does not try to apply its own 1559 if ( empty( $this->query_vars_raw['type'] ) ) { 1560 $this->query_vars_raw['type'] = 'last_joined'; 1561 } 1562 1563 // Set the sort order 1564 add_action( 'bp_pre_user_query', array( $this, 'set_orderby' ) ); 1565 1566 // Set up our populate_extras method 1567 add_action( 'bp_user_query_populate_extras', array( $this, 'populate_group_member_extras' ), 10, 2 ); 1568 } 1569 1570 /** 1571 * Get a list of user_ids to include in the IN clause of the main query. 1572 * 1573 * Overrides BP_User_Query::get_include_ids(), adding our additional 1574 * group-member logic. 1575 * 1576 * @since BuddyPress (1.8.0) 1577 * 1578 * @param array $include Existing group IDs in the $include parameter, 1579 * as calculated in BP_User_Query. 1580 * @return array 1581 */ 1582 public function get_include_ids( $include = array() ) { 1583 // The following args are specific to group member queries, and 1584 // are not present in the query_vars of a normal BP_User_Query. 1585 // We loop through to make sure that defaults are set (though 1586 // values passed to the constructor will, as usual, override 1587 // these defaults). 1588 $this->query_vars = wp_parse_args( $this->query_vars, array( 1589 'group_id' => 0, 1590 'group_role' => array( 'member' ), 1591 'is_confirmed' => true, 1592 'invite_sent' => null, 1593 'inviter_id' => null, 1594 'type' => 'last_joined', 1595 ) ); 1596 1597 $group_member_ids = $this->get_group_member_ids(); 1598 1599 // If the group member query returned no users, bail with an 1600 // array that will guarantee no matches for BP_User_Query 1601 if ( empty( $group_member_ids ) ) { 1602 return array( 0 ); 1603 } 1604 1605 if ( ! empty( $include ) ) { 1606 $group_member_ids = array_intersect( $include, $group_member_ids ); 1607 } 1608 1609 return $group_member_ids; 1610 } 1611 1612 /** 1613 * Get the members of the queried group. 1614 * 1615 * @since BuddyPress (1.8.0) 1616 * 1617 * @return array $ids User IDs of relevant group member ids. 1618 */ 1619 protected function get_group_member_ids() { 1620 global $wpdb; 1621 1622 if ( is_array( $this->group_member_ids ) ) { 1623 return $this->group_member_ids; 1624 } 1625 1626 $bp = buddypress(); 1627 $sql = array( 1628 'select' => "SELECT user_id FROM {$bp->groups->table_name_members}", 1629 'where' => array(), 1630 'orderby' => '', 1631 'order' => '', 1632 ); 1633 1634 /** WHERE clauses *****************************************************/ 1635 1636 // Group id 1637 $sql['where'][] = $wpdb->prepare( "group_id = %d", $this->query_vars['group_id'] ); 1638 1639 // is_confirmed 1640 $is_confirmed = ! empty( $this->query_vars['is_confirmed'] ) ? 1 : 0; 1641 $sql['where'][] = $wpdb->prepare( "is_confirmed = %d", $is_confirmed ); 1642 1643 // invite_sent 1644 if ( ! is_null( $this->query_vars['invite_sent'] ) ) { 1645 $invite_sent = ! empty( $this->query_vars['invite_sent'] ) ? 1 : 0; 1646 $sql['where'][] = $wpdb->prepare( "invite_sent = %d", $invite_sent ); 1647 } 1648 1649 // inviter_id 1650 if ( ! is_null( $this->query_vars['inviter_id'] ) ) { 1651 $inviter_id = $this->query_vars['inviter_id']; 1652 1653 // Empty: inviter_id = 0. (pass false, 0, or empty array) 1654 if ( empty( $inviter_id ) ) { 1655 $sql['where'][] = "inviter_id = 0"; 1656 1657 // The string 'any' matches any non-zero value (inviter_id != 0) 1658 } elseif ( 'any' === $inviter_id ) { 1659 $sql['where'][] = "inviter_id != 0"; 1660 1661 // Assume that a list of inviter IDs has been passed 1662 } else { 1663 // Parse and sanitize 1664 $inviter_ids = wp_parse_id_list( $inviter_id ); 1665 if ( ! empty( $inviter_ids ) ) { 1666 $inviter_ids_sql = implode( ',', $inviter_ids ); 1667 $sql['where'][] = "inviter_id IN ({$inviter_ids_sql})"; 1668 } 1669 } 1670 } 1671 1672 // Role information is stored as follows: admins have 1673 // is_admin = 1, mods have is_mod = 1, banned have is_banned = 1674 // 1, and members have all three set to 0. 1675 $roles = !empty( $this->query_vars['group_role'] ) ? $this->query_vars['group_role'] : array(); 1676 if ( is_string( $roles ) ) { 1677 $roles = explode( ',', $roles ); 1678 } 1679 1680 // Sanitize: Only 'admin', 'mod', 'member', and 'banned' are valid 1681 $allowed_roles = array( 'admin', 'mod', 'member', 'banned' ); 1682 foreach ( $roles as $role_key => $role_value ) { 1683 if ( ! in_array( $role_value, $allowed_roles ) ) { 1684 unset( $roles[ $role_key ] ); 1685 } 1686 } 1687 1688 $roles = array_unique( $roles ); 1689 1690 // When querying for a set of roles containing 'member' (for 1691 // which there is no dedicated is_ column), figure out a list 1692 // of columns *not* to match 1693 $roles_sql = ''; 1694 if ( in_array( 'member', $roles ) ) { 1695 $role_columns = array(); 1696 foreach ( array_diff( $allowed_roles, $roles ) as $excluded_role ) { 1697 $role_columns[] = 'is_' . $excluded_role . ' = 0'; 1698 } 1699 1700 if ( ! empty( $role_columns ) ) { 1701 $roles_sql = '(' . implode( ' AND ', $role_columns ) . ')'; 1702 } 1703 1704 // When querying for a set of roles *not* containing 'member', 1705 // simply construct a list of is_* = 1 clauses 1706 } else { 1707 $role_columns = array(); 1708 foreach ( $roles as $role ) { 1709 $role_columns[] = 'is_' . $role . ' = 1'; 1710 } 1711 1712 if ( ! empty( $role_columns ) ) { 1713 $roles_sql = '(' . implode( ' OR ', $role_columns ) . ')'; 1714 } 1715 } 1716 1717 if ( ! empty( $roles_sql ) ) { 1718 $sql['where'][] = $roles_sql; 1719 } 1720 1721 $sql['where'] = ! empty( $sql['where'] ) ? 'WHERE ' . implode( ' AND ', $sql['where'] ) : ''; 1722 1723 // We fetch group members in order of last_joined, regardless 1724 // of 'type'. If the 'type' value is not 'last_joined' or 1725 // 'first_joined', the order will be overridden in 1726 // BP_Group_Member_Query::set_orderby() 1727 $sql['orderby'] = "ORDER BY date_modified"; 1728 $sql['order'] = 'first_joined' === $this->query_vars['type'] ? 'ASC' : 'DESC'; 1729 1730 $this->group_member_ids = $wpdb->get_col( "{$sql['select']} {$sql['where']} {$sql['orderby']} {$sql['order']}" ); 1731 1732 /** 1733 * Use this filter to build a custom query (such as when you've 1734 * defined a custom 'type'). 1735 */ 1736 $this->group_member_ids = apply_filters( 'bp_group_member_query_group_member_ids', $this->group_member_ids, $this ); 1737 1738 return $this->group_member_ids; 1739 } 1740 1741 /** 1742 * Tell BP_User_Query to order by the order of our query results. 1743 * 1744 * We only override BP_User_Query's native ordering in case of the 1745 * 'last_joined' and 'first_joined' $type parameters. 1746 * 1747 * @param BP_User_Query $query BP_User_Query object. 1748 */ 1749 public function set_orderby( $query ) { 1750 $gm_ids = $this->get_group_member_ids(); 1751 if ( empty( $gm_ids ) ) { 1752 $gm_ids = array( 0 ); 1753 } 1754 1755 // For 'last_joined', 'first_joined', and 'group_activity' 1756 // types, we override the default orderby clause of 1757 // BP_User_Query. In the case of 'group_activity', we perform 1758 // a separate query to get the necessary order. In the case of 1759 // 'last_joined' and 'first_joined', we can trust the order of 1760 // results from BP_Group_Member_Query::get_group_members(). 1761 // In all other cases, we fall through and let BP_User_Query 1762 // do its own (non-group-specific) ordering. 1763 if ( in_array( $query->query_vars['type'], array( 'last_joined', 'first_joined', 'group_activity' ) ) ) { 1764 1765 // Group Activity DESC 1766 if ( 'group_activity' == $query->query_vars['type'] ) { 1767 $gm_ids = $this->get_gm_ids_ordered_by_activity( $query, $gm_ids ); 1768 } 1769 1770 // The first param in the FIELD() clause is the sort column id 1771 $gm_ids = array_merge( array( 'u.id' ), wp_parse_id_list( $gm_ids ) ); 1772 $gm_ids_sql = implode( ',', $gm_ids ); 1773 1774 $query->uid_clauses['orderby'] = "ORDER BY FIELD(" . $gm_ids_sql . ")"; 1775 } 1776 1777 // Prevent this filter from running on future BP_User_Query 1778 // instances on the same page 1779 remove_action( 'bp_pre_user_query', array( $this, 'set_orderby' ) ); 1780 } 1781 1782 /** 1783 * Fetch additional data required in bp_group_has_members() loops. 1784 * 1785 * Additional data fetched: 1786 * 1787 * - is_banned 1788 * - date_modified 1789 * 1790 * @since BuddyPress (1.8.0) 1791 * 1792 * @param BP_User_Query $query BP_User_Query object. Because we're 1793 * filtering the current object, we use $this inside of the 1794 * method instead. 1795 * @param string $user_ids_sql Sanitized, comma-separated string of 1796 * the user ids returned by the main query. 1797 */ 1798 public function populate_group_member_extras( $query, $user_ids_sql ) { 1799 global $wpdb; 1800 1801 $bp = buddypress(); 1802 $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'] ) ); 1803 1804 foreach ( (array) $extras as $extra ) { 1805 if ( isset( $this->results[ $extra->user_id ] ) ) { 1806 // user_id is provided for backward compatibility 1807 $this->results[ $extra->user_id ]->user_id = (int) $extra->user_id; 1808 $this->results[ $extra->user_id ]->is_admin = (int) $extra->is_admin; 1809 $this->results[ $extra->user_id ]->is_mod = (int) $extra->is_mod; 1810 $this->results[ $extra->user_id ]->is_banned = (int) $extra->is_banned; 1811 $this->results[ $extra->user_id ]->date_modified = $extra->date_modified; 1812 $this->results[ $extra->user_id ]->user_title = $extra->user_title; 1813 $this->results[ $extra->user_id ]->comments = $extra->comments; 1814 $this->results[ $extra->user_id ]->invite_sent = (int) $extra->invite_sent; 1815 $this->results[ $extra->user_id ]->inviter_id = (int) $extra->inviter_id; 1816 $this->results[ $extra->user_id ]->is_confirmed = (int) $extra->is_confirmed; 1817 $this->results[ $extra->user_id ]->membership_id = (int) $extra->id; 1818 } 1819 } 1820 1821 // Don't filter other BP_User_Query objects on the same page 1822 remove_action( 'bp_user_query_populate_extras', array( $this, 'populate_group_member_extras' ), 10, 2 ); 1823 } 1824 1825 /** 1826 * Sort user IDs by how recently they have generated activity within a given group. 1827 * 1828 * @since BuddyPress (2.1.0) 1829 * 1830 * @param BP_User_Query $query BP_User_Query object. 1831 * @param array $gm_ids array of group member ids. 1832 * @return array 1833 */ 1834 public function get_gm_ids_ordered_by_activity( $query, $gm_ids = array() ) { 1835 global $wpdb; 1836 1837 if ( empty( $gm_ids ) ) { 1838 return $gm_ids; 1839 } 1840 1841 if ( ! bp_is_active( 'activity' ) ) { 1842 return $gm_ids; 1843 } 1844 1845 $activity_table = buddypress()->activity->table_name; 1846 1847 $sql = array( 1848 'select' => "SELECT user_id, max( date_recorded ) as date_recorded FROM {$activity_table}", 1849 'where' => array(), 1850 'groupby' => 'GROUP BY user_id', 1851 'orderby' => 'ORDER BY date_recorded', 1852 'order' => 'DESC', 1853 ); 1854 1855 $sql['where'] = array( 1856 'user_id IN (' . implode( ',', wp_parse_id_list( $gm_ids ) ) . ')', 1857 'item_id = ' . absint( $query->query_vars['group_id'] ), 1858 $wpdb->prepare( "component = %s", buddypress()->groups->id ), 1859 ); 1860 1861 $sql['where'] = 'WHERE ' . implode( ' AND ', $sql['where'] ); 1862 1863 $group_user_ids = $wpdb->get_results( "{$sql['select']} {$sql['where']} {$sql['groupby']} {$sql['orderby']} {$sql['order']}" ); 1864 1865 return wp_list_pluck( $group_user_ids, 'user_id' ); 1866 } 1867 } 1868 1869 /** 1870 * BuddyPress Group Membership object. 1871 */ 1872 class BP_Groups_Member { 1873 1874 /** 1875 * ID of the membership. 1876 * 1877 * @access public 1878 * @var int 1879 */ 1880 var $id; 1881 1882 /** 1883 * ID of the group associated with the membership. 1884 * 1885 * @access public 1886 * @var int 1887 */ 1888 var $group_id; 1889 1890 /** 1891 * ID of the user associated with the membership. 1892 * 1893 * @access public 1894 * @var int 1895 */ 1896 var $user_id; 1897 1898 /** 1899 * ID of the user whose invitation initiated the membership. 1900 * 1901 * @access public 1902 * @var int 1903 */ 1904 var $inviter_id; 1905 1906 /** 1907 * Whether the member is an admin of the group. 1908 * 1909 * @access public 1910 * @var int 1911 */ 1912 var $is_admin; 1913 1914 /** 1915 * Whether the member is a mod of the group. 1916 * 1917 * @access public 1918 * @var int 1919 */ 1920 var $is_mod; 1921 1922 /** 1923 * Whether the member is banned from the group. 1924 * 1925 * @access public 1926 * @var int 1927 */ 1928 var $is_banned; 1929 1930 /** 1931 * Title used to describe the group member's role in the group. 1932 * 1933 * Eg, 'Group Admin'. 1934 * 1935 * @access public 1936 * @var int 1937 */ 1938 var $user_title; 1939 1940 /** 1941 * Last modified date of the membership. 1942 * 1943 * This value is updated when, eg, invitations are accepted. 1944 * 1945 * @access public 1946 * @var string 1947 */ 1948 var $date_modified; 1949 1950 /** 1951 * Whether the membership has been confirmed. 1952 * 1953 * @access public 1954 * @var int 1955 */ 1956 var $is_confirmed; 1957 1958 /** 1959 * Comments associated with the membership. 1960 * 1961 * In BP core, these are limited to the optional message users can 1962 * include when requesting membership to a private group. 1963 * 1964 * @access public 1965 * @var string 1966 */ 1967 var $comments; 1968 1969 /** 1970 * Whether an invitation has been sent for this membership. 1971 * 1972 * The purpose of this flag is to mark when an invitation has been 1973 * "drafted" (the user has been added via the interface at Send 1974 * Invites), but the Send button has not been pressed, so the 1975 * invitee has not yet been notified. 1976 * 1977 * @access public 1978 * @var int 1979 */ 1980 var $invite_sent; 1981 1982 /** 1983 * WP_User object representing the membership's user. 1984 * 1985 * @access public 1986 * @var WP_User 1987 */ 1988 var $user; 1989 1990 /** 1991 * Constructor method. 1992 * 1993 * @param int $user_id Optional. Along with $group_id, can be used to 1994 * look up a membership. 1995 * @param int $group_id Optional. Along with $user_id, can be used to 1996 * look up a membership. 1997 * @param int $id Optional. The unique ID of the membership object. 1998 * @param bool $populate Whether to populate the properties of the 1999 * located membership. Default: true. 2000 */ 2001 public function __construct( $user_id = 0, $group_id = 0, $id = false, $populate = true ) { 2002 2003 // User and group are not empty, and ID is 2004 if ( !empty( $user_id ) && !empty( $group_id ) && empty( $id ) ) { 2005 $this->user_id = $user_id; 2006 $this->group_id = $group_id; 2007 2008 if ( !empty( $populate ) ) { 2009 $this->populate(); 2010 } 2011 } 2012 2013 // ID is not empty 2014 if ( !empty( $id ) ) { 2015 $this->id = $id; 2016 2017 if ( !empty( $populate ) ) { 2018 $this->populate(); 2019 } 2020 } 2021 } 2022 2023 /** 2024 * Populate the object's properties. 2025 */ 2026 public function populate() { 2027 global $wpdb; 2028 2029 $bp = buddypress(); 2030 2031 if ( $this->user_id && $this->group_id && !$this->id ) 2032 $sql = $wpdb->prepare( "SELECT * FROM {$bp->groups->table_name_members} WHERE user_id = %d AND group_id = %d", $this->user_id, $this->group_id ); 2033 2034 if ( !empty( $this->id ) ) 2035 $sql = $wpdb->prepare( "SELECT * FROM {$bp->groups->table_name_members} WHERE id = %d", $this->id ); 2036 2037 $member = $wpdb->get_row($sql); 2038 2039 if ( !empty( $member ) ) { 2040 $this->id = $member->id; 2041 $this->group_id = $member->group_id; 2042 $this->user_id = $member->user_id; 2043 $this->inviter_id = $member->inviter_id; 2044 $this->is_admin = $member->is_admin; 2045 $this->is_mod = $member->is_mod; 2046 $this->is_banned = $member->is_banned; 2047 $this->user_title = $member->user_title; 2048 $this->date_modified = $member->date_modified; 2049 $this->is_confirmed = $member->is_confirmed; 2050 $this->comments = $member->comments; 2051 $this->invite_sent = $member->invite_sent; 2052 2053 $this->user = new BP_Core_User( $this->user_id ); 2054 } 2055 } 2056 2057 /** 2058 * Save the membership data to the database. 2059 * 2060 * @return bool True on success, false on failure. 2061 */ 2062 public function save() { 2063 global $wpdb; 2064 2065 $bp = buddypress(); 2066 2067 $this->user_id = apply_filters( 'groups_member_user_id_before_save', $this->user_id, $this->id ); 2068 $this->group_id = apply_filters( 'groups_member_group_id_before_save', $this->group_id, $this->id ); 2069 $this->inviter_id = apply_filters( 'groups_member_inviter_id_before_save', $this->inviter_id, $this->id ); 2070 $this->is_admin = apply_filters( 'groups_member_is_admin_before_save', $this->is_admin, $this->id ); 2071 $this->is_mod = apply_filters( 'groups_member_is_mod_before_save', $this->is_mod, $this->id ); 2072 $this->is_banned = apply_filters( 'groups_member_is_banned_before_save', $this->is_banned, $this->id ); 2073 $this->user_title = apply_filters( 'groups_member_user_title_before_save', $this->user_title, $this->id ); 2074 $this->date_modified = apply_filters( 'groups_member_date_modified_before_save', $this->date_modified, $this->id ); 2075 $this->is_confirmed = apply_filters( 'groups_member_is_confirmed_before_save', $this->is_confirmed, $this->id ); 2076 $this->comments = apply_filters( 'groups_member_comments_before_save', $this->comments, $this->id ); 2077 $this->invite_sent = apply_filters( 'groups_member_invite_sent_before_save', $this->invite_sent, $this->id ); 2078 2079 do_action_ref_array( 'groups_member_before_save', array( &$this ) ); 2080 2081 if ( !empty( $this->id ) ) { 2082 $sql = $wpdb->prepare( "UPDATE {$bp->groups->table_name_members} SET inviter_id = %d, is_admin = %d, is_mod = %d, is_banned = %d, user_title = %s, date_modified = %s, is_confirmed = %d, comments = %s, invite_sent = %d WHERE id = %d", $this->inviter_id, $this->is_admin, $this->is_mod, $this->is_banned, $this->user_title, $this->date_modified, $this->is_confirmed, $this->comments, $this->invite_sent, $this->id ); 2083 } else { 2084 // Ensure that user is not already a member of the group before inserting 2085 if ( $wpdb->get_var( $wpdb->prepare( "SELECT id FROM {$bp->groups->table_name_members} WHERE user_id = %d AND group_id = %d AND is_confirmed = 1 LIMIT 1", $this->user_id, $this->group_id ) ) ) { 2086 return false; 2087 } 2088 2089 $sql = $wpdb->prepare( "INSERT INTO {$bp->groups->table_name_members} ( user_id, group_id, inviter_id, is_admin, is_mod, is_banned, user_title, date_modified, is_confirmed, comments, invite_sent ) VALUES ( %d, %d, %d, %d, %d, %d, %s, %s, %d, %s, %d )", $this->user_id, $this->group_id, $this->inviter_id, $this->is_admin, $this->is_mod, $this->is_banned, $this->user_title, $this->date_modified, $this->is_confirmed, $this->comments, $this->invite_sent ); 2090 } 2091 2092 if ( !$wpdb->query( $sql ) ) 2093 return false; 2094 2095 $this->id = $wpdb->insert_id; 2096 2097 // Update the user's group count 2098 self::refresh_total_group_count_for_user( $this->user_id ); 2099 2100 // Update the group's member count 2101 self::refresh_total_member_count_for_group( $this->group_id ); 2102 2103 do_action_ref_array( 'groups_member_after_save', array( &$this ) ); 2104 2105 return true; 2106 } 2107 2108 /** 2109 * Promote a member to a new status. 2110 * 2111 * @param string $status The new status. 'mod' or 'admin'. 2112 * @return bool True on success, false on failure. 2113 */ 2114 public function promote( $status = 'mod' ) { 2115 if ( 'mod' == $status ) { 2116 $this->is_admin = 0; 2117 $this->is_mod = 1; 2118 $this->user_title = __( 'Group Mod', 'buddypress' ); 2119 } 2120 2121 if ( 'admin' == $status ) { 2122 $this->is_admin = 1; 2123 $this->is_mod = 0; 2124 $this->user_title = __( 'Group Admin', 'buddypress' ); 2125 } 2126 2127 return $this->save(); 2128 } 2129 2130 /** 2131 * Demote membership to Member status (non-admin, non-mod). 2132 * 2133 * @return bool True on success, false on failure. 2134 */ 2135 public function demote() { 2136 $this->is_mod = 0; 2137 $this->is_admin = 0; 2138 $this->user_title = false; 2139 2140 return $this->save(); 2141 } 2142 2143 /** 2144 * Ban the user from the group. 2145 * 2146 * @return bool True on success, false on failure. 2147 */ 2148 public function ban() { 2149 if ( !empty( $this->is_admin ) ) 2150 return false; 2151 2152 $this->is_mod = 0; 2153 $this->is_banned = 1; 2154 2155 return $this->save(); 2156 } 2157 2158 /** 2159 * Unban the user from the group. 2160 * 2161 * @return bool True on success, false on failure. 2162 */ 2163 public function unban() { 2164 if ( !empty( $this->is_admin ) ) 2165 return false; 2166 2167 $this->is_banned = 0; 2168 2169 return $this->save(); 2170 } 2171 2172 /** 2173 * Mark a pending invitation as accepted. 2174 */ 2175 public function accept_invite() { 2176 $this->inviter_id = 0; 2177 $this->is_confirmed = 1; 2178 $this->date_modified = bp_core_current_time(); 2179 } 2180 2181 /** 2182 * Confirm a membership request. 2183 */ 2184 public function accept_request() { 2185 $this->is_confirmed = 1; 2186 $this->date_modified = bp_core_current_time(); 2187 } 2188 2189 /** 2190 * Remove the current membership. 2191 * 2192 * @return bool True on success, false on failure. 2193 */ 2194 public function remove() { 2195 global $wpdb; 2196 2197 $bp = buddypress(); 2198 $sql = $wpdb->prepare( "DELETE FROM {$bp->groups->table_name_members} WHERE user_id = %d AND group_id = %d", $this->user_id, $this->group_id ); 2199 2200 if ( !$result = $wpdb->query( $sql ) ) 2201 return false; 2202 2203 // Update the user's group count 2204 self::refresh_total_group_count_for_user( $this->user_id ); 2205 2206 // Update the group's member count 2207 self::refresh_total_member_count_for_group( $this->group_id ); 2208 2209 return $result; 2210 } 2211 2212 /** Static Methods ****************************************************/ 2213 2214 /** 2215 * Refresh the total_group_count for a user. 2216 * 2217 * @since BuddyPress (1.8.0) 2218 * 2219 * @param int $user_id ID of the user. 2220 * @return bool True on success, false on failure. 2221 */ 2222 public static function refresh_total_group_count_for_user( $user_id ) { 2223 return bp_update_user_meta( $user_id, 'total_group_count', (int) self::total_group_count( $user_id ) ); 2224 } 2225 2226 /** 2227 * Refresh the total_member_count for a group. 2228 * 2229 * @since BuddyPress (1.8.0) 2230 * 2231 * @param int $group_id ID of the group. 2232 * @return bool True on success, false on failure. 2233 */ 2234 public static function refresh_total_member_count_for_group( $group_id ) { 2235 return groups_update_groupmeta( $group_id, 'total_member_count', (int) BP_Groups_Group::get_total_member_count( $group_id ) ); 2236 } 2237 2238 /** 2239 * Delete a membership, based on user + group IDs. 2240 * 2241 * @param int $user_id ID of the user. 2242 * @param int $group_id ID of the group. 2243 * @return True on success, false on failure. 2244 */ 2245 public static function delete( $user_id, $group_id ) { 2246 global $wpdb; 2247 2248 $bp = buddypress(); 2249 $remove = $wpdb->query( $wpdb->prepare( "DELETE FROM {$bp->groups->table_name_members} WHERE user_id = %d AND group_id = %d", $user_id, $group_id ) ); 2250 2251 // Update the user's group count 2252 self::refresh_total_group_count_for_user( $user_id ); 2253 2254 // Update the group's member count 2255 self::refresh_total_member_count_for_group( $group_id ); 2256 2257 return $remove; 2258 } 2259 2260 /** 2261 * Get the IDs of the groups of which a specified user is a member. 2262 * 2263 * @param int $user_id ID of the user. 2264 * @param int $limit Optional. Max number of results to return. 2265 * Default: false (no limit). 2266 * @param int $page Optional. Page offset of results to return. 2267 * Default: false (no limit). 2268 * @return array { 2269 * @type array $groups Array of groups returned by paginated query. 2270 * @type int $total Count of groups matching query. 2271 * } 2272 */ 2273 public static function get_group_ids( $user_id, $limit = false, $page = false ) { 2274 global $wpdb; 2275 2276 $pag_sql = ''; 2277 if ( !empty( $limit ) && !empty( $page ) ) 2278 $pag_sql = $wpdb->prepare( " LIMIT %d, %d", intval( ( $page - 1 ) * $limit), intval( $limit ) ); 2279 2280 $bp = buddypress(); 2281 2282 // If the user is logged in and viewing their own groups, we can show hidden and private groups 2283 if ( $user_id != bp_loggedin_user_id() ) { 2284 $group_sql = $wpdb->prepare( "SELECT DISTINCT m.group_id FROM {$bp->groups->table_name_members} m, {$bp->groups->table_name} g WHERE g.status != 'hidden' AND m.user_id = %d AND m.is_confirmed = 1 AND m.is_banned = 0{$pag_sql}", $user_id ); 2285 $total_groups = $wpdb->get_var( $wpdb->prepare( "SELECT COUNT(DISTINCT m.group_id) FROM {$bp->groups->table_name_members} m, {$bp->groups->table_name} g WHERE g.status != 'hidden' AND m.user_id = %d AND m.is_confirmed = 1 AND m.is_banned = 0", $user_id ) ); 2286 } else { 2287 $group_sql = $wpdb->prepare( "SELECT DISTINCT group_id FROM {$bp->groups->table_name_members} WHERE user_id = %d AND is_confirmed = 1 AND is_banned = 0{$pag_sql}", $user_id ); 2288 $total_groups = $wpdb->get_var( $wpdb->prepare( "SELECT COUNT(DISTINCT group_id) FROM {$bp->groups->table_name_members} WHERE user_id = %d AND is_confirmed = 1 AND is_banned = 0", $user_id ) ); 2289 } 2290 2291 $groups = $wpdb->get_col( $group_sql ); 2292 2293 return array( 'groups' => $groups, 'total' => (int) $total_groups ); 2294 } 2295 2296 /** 2297 * Get the IDs of the groups of which a specified user is a member, sorted by the date joined. 2298 * 2299 * @param int $user_id ID of the user. 2300 * @param int $limit Optional. Max number of results to return. 2301 * Default: false (no limit). 2302 * @param int $page Optional. Page offset of results to return. 2303 * Default: false (no limit). 2304 * @param string $filter Optional. Limit results to groups whose name or 2305 * description field matches search terms. 2306 * @return array { 2307 * @type array $groups Array of groups returned by paginated query. 2308 * @type int $total Count of groups matching query. 2309 * } 2310 */ 2311 public static function get_recently_joined( $user_id, $limit = false, $page = false, $filter = false ) { 2312 global $wpdb; 2313 2314 $user_id_sql = $pag_sql = $hidden_sql = $filter_sql = ''; 2315 2316 $user_id_sql = $wpdb->prepare( 'm.user_id = %d', $user_id ); 2317 2318 if ( !empty( $limit ) && !empty( $page ) ) 2319 $pag_sql = $wpdb->prepare( " LIMIT %d, %d", intval( ( $page - 1 ) * $limit), intval( $limit ) ); 2320 2321 if ( !empty( $filter ) ) { 2322 $search_terms_like = '%' . bp_esc_like( $filter ) . '%'; 2323 $filter_sql = $wpdb->prepare( " AND ( g.name LIKE %s OR g.description LIKE %s )", $search_terms_like, $search_terms_like ); 2324 } 2325 2326 if ( $user_id != bp_loggedin_user_id() ) 2327 $hidden_sql = " AND g.status != 'hidden'"; 2328 2329 $bp = buddypress(); 2330 2331 $paged_groups = $wpdb->get_results( "SELECT g.*, gm1.meta_value as total_member_count, gm2.meta_value as last_activity FROM {$bp->groups->table_name_groupmeta} gm1, {$bp->groups->table_name_groupmeta} gm2, {$bp->groups->table_name_members} m, {$bp->groups->table_name} g WHERE g.id = m.group_id AND g.id = gm1.group_id AND g.id = gm2.group_id AND gm2.meta_key = 'last_activity' AND gm1.meta_key = 'total_member_count'{$hidden_sql}{$filter_sql} AND {$user_id_sql} AND m.is_confirmed = 1 AND m.is_banned = 0 ORDER BY m.date_modified DESC {$pag_sql}" ); 2332 $total_groups = $wpdb->get_var( "SELECT COUNT(DISTINCT m.group_id) FROM {$bp->groups->table_name_members} m, {$bp->groups->table_name} g WHERE m.group_id = g.id{$hidden_sql}{$filter_sql} AND {$user_id_sql} AND m.is_banned = 0 AND m.is_confirmed = 1 ORDER BY m.date_modified DESC" ); 2333 2334 return array( 'groups' => $paged_groups, 'total' => $total_groups ); 2335 } 2336 2337 /** 2338 * Get the IDs of the groups of which a specified user is an admin. 2339 * 2340 * @param int $user_id ID of the user. 2341 * @param int $limit Optional. Max number of results to return. 2342 * Default: false (no limit). 2343 * @param int $page Optional. Page offset of results to return. 2344 * Default: false (no limit). 2345 * @param string $filter Optional. Limit results to groups whose name or 2346 * description field matches search terms. 2347 * @return array { 2348 * @type array $groups Array of groups returned by paginated query. 2349 * @type int $total Count of groups matching query. 2350 * } 2351 */ 2352 public static function get_is_admin_of( $user_id, $limit = false, $page = false, $filter = false ) { 2353 global $wpdb; 2354 2355 $user_id_sql = $pag_sql = $hidden_sql = $filter_sql = ''; 2356 2357 $user_id_sql = $wpdb->prepare( 'm.user_id = %d', $user_id ); 2358 2359 if ( !empty( $limit ) && !empty( $page ) ) 2360 $pag_sql = $wpdb->prepare( " LIMIT %d, %d", intval( ( $page - 1 ) * $limit), intval( $limit ) ); 2361 2362 if ( !empty( $filter ) ) { 2363 $search_terms_like = '%' . bp_esc_like( $filter ) . '%'; 2364 $filter_sql = $wpdb->prepare( " AND ( g.name LIKE %s OR g.description LIKE %s )", $search_terms_like, $search_terms_like ); 2365 } 2366 2367 if ( $user_id != bp_loggedin_user_id() ) 2368 $hidden_sql = " AND g.status != 'hidden'"; 2369 2370 $bp = buddypress(); 2371 2372 $paged_groups = $wpdb->get_results( "SELECT g.*, gm1.meta_value as total_member_count, gm2.meta_value as last_activity FROM {$bp->groups->table_name_groupmeta} gm1, {$bp->groups->table_name_groupmeta} gm2, {$bp->groups->table_name_members} m, {$bp->groups->table_name} g WHERE g.id = m.group_id AND g.id = gm1.group_id AND g.id = gm2.group_id AND gm2.meta_key = 'last_activity' AND gm1.meta_key = 'total_member_count'{$hidden_sql}{$filter_sql} AND {$user_id_sql} AND m.is_confirmed = 1 AND m.is_banned = 0 AND m.is_admin = 1 ORDER BY m.date_modified ASC {$pag_sql}" ); 2373 $total_groups = $wpdb->get_var( "SELECT COUNT(DISTINCT m.group_id) FROM {$bp->groups->table_name_members} m, {$bp->groups->table_name} g WHERE m.group_id = g.id{$hidden_sql}{$filter_sql} AND {$user_id_sql} AND m.is_confirmed = 1 AND m.is_banned = 0 AND m.is_admin = 1 ORDER BY date_modified ASC" ); 2374 2375 return array( 'groups' => $paged_groups, 'total' => $total_groups ); 2376 } 2377 2378 /** 2379 * Get the IDs of the groups of which a specified user is a moderator. 2380 * 2381 * @param int $user_id ID of the user. 2382 * @param int $limit Optional. Max number of results to return. 2383 * Default: false (no limit). 2384 * @param int $page Optional. Page offset of results to return. 2385 * Default: false (no limit). 2386 * @param string $filter Optional. Limit results to groups whose name or 2387 * description field matches search terms. 2388 * @return array { 2389 * @type array $groups Array of groups returned by paginated query. 2390 * @type int $total Count of groups matching query. 2391 * } 2392 */ 2393 public static function get_is_mod_of( $user_id, $limit = false, $page = false, $filter = false ) { 2394 global $wpdb; 2395 2396 $user_id_sql = $pag_sql = $hidden_sql = $filter_sql = ''; 2397 2398 $user_id_sql = $wpdb->prepare( 'm.user_id = %d', $user_id ); 2399 2400 if ( !empty( $limit ) && !empty( $page ) ) 2401 $pag_sql = $wpdb->prepare( " LIMIT %d, %d", intval( ( $page - 1 ) * $limit), intval( $limit ) ); 2402 2403 if ( !empty( $filter ) ) { 2404 $search_terms_like = '%' . bp_esc_like( $filter ) . '%'; 2405 $filter_sql = $wpdb->prepare( " AND ( g.name LIKE %s OR g.description LIKE %s )", $search_terms_like, $search_terms_like ); 2406 } 2407 2408 if ( $user_id != bp_loggedin_user_id() ) 2409 $hidden_sql = " AND g.status != 'hidden'"; 2410 2411 $bp = buddypress(); 2412 2413 $paged_groups = $wpdb->get_results( "SELECT g.*, gm1.meta_value as total_member_count, gm2.meta_value as last_activity FROM {$bp->groups->table_name_groupmeta} gm1, {$bp->groups->table_name_groupmeta} gm2, {$bp->groups->table_name_members} m, {$bp->groups->table_name} g WHERE g.id = m.group_id AND g.id = gm1.group_id AND g.id = gm2.group_id AND gm2.meta_key = 'last_activity' AND gm1.meta_key = 'total_member_count'{$hidden_sql}{$filter_sql} AND {$user_id_sql} AND m.is_confirmed = 1 AND m.is_banned = 0 AND m.is_mod = 1 ORDER BY m.date_modified ASC {$pag_sql}" ); 2414 $total_groups = $wpdb->get_var( "SELECT COUNT(DISTINCT m.group_id) FROM {$bp->groups->table_name_members} m, {$bp->groups->table_name} g WHERE m.group_id = g.id{$hidden_sql}{$filter_sql} AND {$user_id_sql} AND m.is_confirmed = 1 AND m.is_banned = 0 AND m.is_mod = 1 ORDER BY date_modified ASC" ); 2415 2416 return array( 'groups' => $paged_groups, 'total' => $total_groups ); 2417 } 2418 2419 /** 2420 * Get the count of groups of which the specified user is a member. 2421 * 2422 * @param int $user_id Optional. Default: ID of the displayed user. 2423 * @return int Group count. 2424 */ 2425 public static function total_group_count( $user_id = 0 ) { 2426 global $wpdb; 2427 2428 if ( empty( $user_id ) ) 2429 $user_id = bp_displayed_user_id(); 2430 2431 $bp = buddypress(); 2432 2433 if ( $user_id != bp_loggedin_user_id() && !bp_current_user_can( 'bp_moderate' ) ) { 2434 return $wpdb->get_var( $wpdb->prepare( "SELECT COUNT(DISTINCT m.group_id) FROM {$bp->groups->table_name_members} m, {$bp->groups->table_name} g WHERE m.group_id = g.id AND g.status != 'hidden' AND m.user_id = %d AND m.is_confirmed = 1 AND m.is_banned = 0", $user_id ) ); 2435 } else { 2436 return $wpdb->get_var( $wpdb->prepare( "SELECT COUNT(DISTINCT m.group_id) FROM {$bp->groups->table_name_members} m, {$bp->groups->table_name} g WHERE m.group_id = g.id AND m.user_id = %d AND m.is_confirmed = 1 AND m.is_banned = 0", $user_id ) ); 2437 } 2438 } 2439 2440 /** 2441 * Get a user's outstanding group invitations. 2442 * 2443 * @param int $user_id ID of the invitee. 2444 * @param int $limit Optional. Max number of results to return. 2445 * Default: false (no limit). 2446 * @param int $page Optional. Page offset of results to return. 2447 * Default: false (no limit). 2448 * @param string|array $exclude Optional. Array or comma-separated list 2449 * of group IDs to exclude from results. 2450 * @return array { 2451 * @type array $groups Array of groups returned by paginated query. 2452 * @type int $total Count of groups matching query. 2453 * } 2454 */ 2455 public static function get_invites( $user_id, $limit = false, $page = false, $exclude = false ) { 2456 global $wpdb; 2457 2458 $pag_sql = ( !empty( $limit ) && !empty( $page ) ) ? $wpdb->prepare( " LIMIT %d, %d", intval( ( $page - 1 ) * $limit), intval( $limit ) ) : ''; 2459 2460 if ( !empty( $exclude ) ) { 2461 $exclude = implode( ',', wp_parse_id_list( $exclude ) ); 2462 $exclude_sql = " AND g.id NOT IN ({$exclude})"; 2463 } else { 2464 $exclude_sql = ''; 2465 } 2466 2467 $bp = buddypress(); 2468 2469 $paged_groups = $wpdb->get_results( $wpdb->prepare( "SELECT g.*, gm1.meta_value as total_member_count, gm2.meta_value as last_activity FROM {$bp->groups->table_name_groupmeta} gm1, {$bp->groups->table_name_groupmeta} gm2, {$bp->groups->table_name_members} m, {$bp->groups->table_name} g WHERE g.id = m.group_id AND g.id = gm1.group_id AND g.id = gm2.group_id AND gm2.meta_key = 'last_activity' AND gm1.meta_key = 'total_member_count' AND m.is_confirmed = 0 AND m.inviter_id != 0 AND m.invite_sent = 1 AND m.user_id = %d {$exclude_sql} ORDER BY m.date_modified ASC {$pag_sql}", $user_id ) ); 2470 2471 return array( 'groups' => $paged_groups, 'total' => self::get_invite_count_for_user( $user_id ) ); 2472 } 2473 2474 /** 2475 * Gets the total group invite count for a user. 2476 * 2477 * @since BuddyPress (2.0.0) 2478 * 2479 * @param int $user_id The user ID 2480 * @return int 2481 */ 2482 public static function get_invite_count_for_user( $user_id = 0 ) { 2483 global $wpdb; 2484 2485 $bp = buddypress(); 2486 2487 $count = wp_cache_get( $user_id, 'bp_group_invite_count' ); 2488 2489 if ( false === $count ) { 2490 $count = (int) $wpdb->get_var( $wpdb->prepare( "SELECT COUNT(DISTINCT m.group_id) FROM {$bp->groups->table_name_members} m, {$bp->groups->table_name} g WHERE m.group_id = g.id AND m.is_confirmed = 0 AND m.inviter_id != 0 AND m.invite_sent = 1 AND m.user_id = %d", $user_id ) ); 2491 wp_cache_set( $user_id, $count, 'bp_group_invite_count' ); 2492 } 2493 2494 return $count; 2495 } 2496 2497 /** 2498 * Check whether a user has an outstanding invitation to a given group. 2499 * 2500 * @param int $user_id ID of the potential invitee. 2501 * @param int $group_id ID of the group. 2502 * @param string $type If 'sent', results are limited to those 2503 * invitations that have actually been sent (non-draft). 2504 * Default: 'sent'. 2505 * @return int|null The ID of the invitation if found, otherwise null. 2506 */ 2507 public static function check_has_invite( $user_id, $group_id, $type = 'sent' ) { 2508 global $wpdb; 2509 2510 if ( empty( $user_id ) ) 2511 return false; 2512 2513 $bp = buddypress(); 2514 $sql = "SELECT id FROM {$bp->groups->table_name_members} WHERE user_id = %d AND group_id = %d AND is_confirmed = 0 AND inviter_id != 0"; 2515 2516 if ( 'sent' == $type ) 2517 $sql .= " AND invite_sent = 1"; 2518 2519 return $wpdb->get_var( $wpdb->prepare( $sql, $user_id, $group_id ) ); 2520 } 2521 2522 /** 2523 * Delete an invitation, by specifying user ID and group ID. 2524 * 2525 * @param int $user_id ID of the user. 2526 * @param int $group_id ID of the group. 2527 * @return int Number of records deleted. 2528 */ 2529 public static function delete_invite( $user_id, $group_id ) { 2530 global $wpdb; 2531 2532 if ( empty( $user_id ) ) 2533 return false; 2534 2535 $bp = buddypress(); 2536 2537 return $wpdb->query( $wpdb->prepare( "DELETE FROM {$bp->groups->table_name_members} WHERE user_id = %d AND group_id = %d AND is_confirmed = 0 AND inviter_id != 0 AND invite_sent = 1", $user_id, $group_id ) ); 2538 } 2539 2540 /** 2541 * Delete an unconfirmed membership request, by user ID and group ID. 2542 * 2543 * @param int $user_id ID of the user. 2544 * @param int $group_id ID of the group. 2545 * @return int Number of records deleted. 2546 */ 2547 public static function delete_request( $user_id, $group_id ) { 2548 global $wpdb; 2549 2550 if ( empty( $user_id ) ) 2551 return false; 2552 2553 $bp = buddypress(); 2554 2555 return $wpdb->query( $wpdb->prepare( "DELETE FROM {$bp->groups->table_name_members} WHERE user_id = %d AND group_id = %d AND is_confirmed = 0 AND inviter_id = 0 AND invite_sent = 0", $user_id, $group_id ) ); 2556 } 2557 2558 /** 2559 * Check whether a user is an admin of a given group. 2560 * 2561 * @param int $user_id ID of the user. 2562 * @param int $group_id ID of the group. 2563 * @param int|null ID of the membership if the user is an admin, 2564 * otherwise null. 2565 */ 2566 public static function check_is_admin( $user_id, $group_id ) { 2567 global $wpdb; 2568 2569 if ( empty( $user_id ) ) 2570 return false; 2571 2572 $bp = buddypress(); 2573 2574 return $wpdb->query( $wpdb->prepare( "SELECT id FROM {$bp->groups->table_name_members} WHERE user_id = %d AND group_id = %d AND is_admin = 1 AND is_banned = 0", $user_id, $group_id ) ); 2575 } 2576 2577 /** 2578 * Check whether a user is a mod of a given group. 2579 * 2580 * @param int $user_id ID of the user. 2581 * @param int $group_id ID of the group. 2582 * @param int|null ID of the membership if the user is a mod, 2583 * otherwise null. 2584 */ 2585 public static function check_is_mod( $user_id, $group_id ) { 2586 global $wpdb; 2587 2588 if ( empty( $user_id ) ) 2589 return false; 2590 2591 $bp = buddypress(); 2592 2593 return $wpdb->query( $wpdb->prepare( "SELECT id FROM {$bp->groups->table_name_members} WHERE user_id = %d AND group_id = %d AND is_mod = 1 AND is_banned = 0", $user_id, $group_id ) ); 2594 } 2595 2596 /** 2597 * Check whether a user is a member of a given group. 2598 * 2599 * @param int $user_id ID of the user. 2600 * @param int $group_id ID of the group. 2601 * @param int|null ID of the membership if the user is a member, 2602 * otherwise null. 2603 */ 2604 public static function check_is_member( $user_id, $group_id ) { 2605 global $wpdb; 2606 2607 if ( empty( $user_id ) ) 2608 return false; 2609 2610 $bp = buddypress(); 2611 2612 return $wpdb->query( $wpdb->prepare( "SELECT id FROM {$bp->groups->table_name_members} WHERE user_id = %d AND group_id = %d AND is_confirmed = 1 AND is_banned = 0", $user_id, $group_id ) ); 2613 } 2614 2615 /** 2616 * Check whether a user is banned from a given group. 2617 * 2618 * @param int $user_id ID of the user. 2619 * @param int $group_id ID of the group. 2620 * @param int|null ID of the membership if the user is banned, 2621 * otherwise null. 2622 */ 2623 public static function check_is_banned( $user_id, $group_id ) { 2624 global $wpdb; 2625 2626 if ( empty( $user_id ) ) 2627 return false; 2628 2629 $bp = buddypress(); 2630 2631 return $wpdb->get_var( $wpdb->prepare( "SELECT is_banned FROM {$bp->groups->table_name_members} WHERE user_id = %d AND group_id = %d", $user_id, $group_id ) ); 2632 } 2633 2634 /** 2635 * Is the specified user the creator of the group? 2636 * 2637 * @since BuddyPress (1.2.6) 2638 * 2639 * @param int $user_id ID of the user. 2640 * @param int $group_id ID of the group. 2641 * @return int|null ID of the group if the user is the creator, 2642 * otherwise false. 2643 */ 2644 public static function check_is_creator( $user_id, $group_id ) { 2645 global $wpdb; 2646 2647 if ( empty( $user_id ) ) 2648 return false; 2649 2650 $bp = buddypress(); 2651 2652 return $wpdb->get_var( $wpdb->prepare( "SELECT id FROM {$bp->groups->table_name} WHERE creator_id = %d AND id = %d", $user_id, $group_id ) ); 2653 } 2654 2655 /** 2656 * Check whether a user has an outstanding membership request for a given group. 2657 * 2658 * @param int $user_id ID of the user. 2659 * @param int $group_id ID of the group. 2660 * @return int|null ID of the membership if found, otherwise false. 2661 */ 2662 public static function check_for_membership_request( $user_id, $group_id ) { 2663 global $wpdb; 2664 2665 if ( empty( $user_id ) ) 2666 return false; 2667 2668 $bp = buddypress(); 2669 2670 return $wpdb->query( $wpdb->prepare( "SELECT id FROM {$bp->groups->table_name_members} WHERE user_id = %d AND group_id = %d AND is_confirmed = 0 AND is_banned = 0 AND inviter_id = 0", $user_id, $group_id ) ); 2671 } 2672 2673 /** 2674 * Get a list of randomly selected IDs of groups that the member belongs to. 2675 * 2676 * @param int $user_id ID of the user. 2677 * @param int $total_groups Max number of group IDs to return. Default: 5. 2678 * @return array Group IDs. 2679 */ 2680 public static function get_random_groups( $user_id = 0, $total_groups = 5 ) { 2681 global $wpdb; 2682 2683 $bp = buddypress(); 2684 2685 // If the user is logged in and viewing their random groups, we can show hidden and private groups 2686 if ( bp_is_my_profile() ) { 2687 return $wpdb->get_col( $wpdb->prepare( "SELECT DISTINCT group_id FROM {$bp->groups->table_name_members} WHERE user_id = %d AND is_confirmed = 1 AND is_banned = 0 ORDER BY rand() LIMIT %d", $user_id, $total_groups ) ); 2688 } else { 2689 return $wpdb->get_col( $wpdb->prepare( "SELECT DISTINCT m.group_id FROM {$bp->groups->table_name_members} m, {$bp->groups->table_name} g WHERE m.group_id = g.id AND g.status != 'hidden' AND m.user_id = %d AND m.is_confirmed = 1 AND m.is_banned = 0 ORDER BY rand() LIMIT %d", $user_id, $total_groups ) ); 2690 } 2691 } 2692 2693 /** 2694 * Get the IDs of all a given group's members. 2695 * 2696 * @param int $group_id ID of the group. 2697 * @return array IDs of all group members. 2698 */ 2699 public static function get_group_member_ids( $group_id ) { 2700 global $wpdb; 2701 2702 $bp = buddypress(); 2703 2704 return $wpdb->get_col( $wpdb->prepare( "SELECT user_id FROM {$bp->groups->table_name_members} WHERE group_id = %d AND is_confirmed = 1 AND is_banned = 0", $group_id ) ); 2705 } 2706 2707 /** 2708 * Get a list of all a given group's admins. 2709 * 2710 * @param int $group_id ID of the group. 2711 * @return array Info about group admins (user_id + date_modified). 2712 */ 2713 public static function get_group_administrator_ids( $group_id ) { 2714 global $wpdb; 2715 2716 $group_admins = wp_cache_get( $group_id, 'bp_group_admins' ); 2717 2718 if ( false === $group_admins ) { 2719 $bp = buddypress(); 2720 $group_admins = $wpdb->get_results( $wpdb->prepare( "SELECT user_id, date_modified FROM {$bp->groups->table_name_members} WHERE group_id = %d AND is_admin = 1 AND is_banned = 0", $group_id ) ); 2721 2722 wp_cache_set( $group_id, $group_admins, 'bp_group_admins' ); 2723 } 2724 2725 return $group_admins; 2726 } 2727 2728 /** 2729 * Get a list of all a given group's moderators. 2730 * 2731 * @param int $group_id ID of the group. 2732 * @return array Info about group mods (user_id + date_modified). 2733 */ 2734 public static function get_group_moderator_ids( $group_id ) { 2735 global $wpdb; 2736 2737 $bp = buddypress(); 2738 2739 return $wpdb->get_results( $wpdb->prepare( "SELECT user_id, date_modified FROM {$bp->groups->table_name_members} WHERE group_id = %d AND is_mod = 1 AND is_banned = 0", $group_id ) ); 2740 } 2741 2742 /** 2743 * Get the IDs users with outstanding membership requests to the group. 2744 * 2745 * @param int $group_id ID of the group. 2746 * @return array IDs of users with outstanding membership requests. 2747 */ 2748 public static function get_all_membership_request_user_ids( $group_id ) { 2749 global $wpdb; 2750 2751 $bp = buddypress(); 2752 2753 return $wpdb->get_col( $wpdb->prepare( "SELECT user_id FROM {$bp->groups->table_name_members} WHERE group_id = %d AND is_confirmed = 0 AND inviter_id = 0", $group_id ) ); 2754 } 2755 2756 /** 2757 * Get members of a group. 2758 * 2759 * @deprecated BuddyPress (1.8.0) 2760 */ 2761 public static function get_all_for_group( $group_id, $limit = false, $page = false, $exclude_admins_mods = true, $exclude_banned = true, $exclude = false ) { 2762 global $wpdb; 2763 2764 _deprecated_function( __METHOD__, '1.8', 'BP_Group_Member_Query' ); 2765 2766 $pag_sql = ''; 2767 if ( !empty( $limit ) && !empty( $page ) ) 2768 $pag_sql = $wpdb->prepare( "LIMIT %d, %d", intval( ( $page - 1 ) * $limit), intval( $limit ) ); 2769 2770 $exclude_admins_sql = ''; 2771 if ( !empty( $exclude_admins_mods ) ) 2772 $exclude_admins_sql = "AND is_admin = 0 AND is_mod = 0"; 2773 2774 $banned_sql = ''; 2775 if ( !empty( $exclude_banned ) ) 2776 $banned_sql = " AND is_banned = 0"; 2777 2778 $exclude_sql = ''; 2779 if ( !empty( $exclude ) ) { 2780 $exclude = implode( ',', wp_parse_id_list( $exclude ) ); 2781 $exclude_sql = " AND m.user_id NOT IN ({$exclude})"; 2782 } 2783 2784 $bp = buddypress(); 2785 2786 if ( bp_is_active( 'xprofile' ) ) { 2787 $members = $wpdb->get_results( apply_filters( 'bp_group_members_user_join_filter', $wpdb->prepare( "SELECT m.user_id, m.date_modified, m.is_banned, u.user_login, u.user_nicename, u.user_email, pd.value as display_name FROM {$bp->groups->table_name_members} m, {$wpdb->users} u, {$bp->profile->table_name_data} pd WHERE u.ID = m.user_id AND u.ID = pd.user_id AND pd.field_id = 1 AND group_id = %d AND is_confirmed = 1 {$banned_sql} {$exclude_admins_sql} {$exclude_sql} ORDER BY m.date_modified DESC {$pag_sql}", $group_id ) ) ); 2788 } else { 2789 $members = $wpdb->get_results( apply_filters( 'bp_group_members_user_join_filter', $wpdb->prepare( "SELECT m.user_id, m.date_modified, m.is_banned, u.user_login, u.user_nicename, u.user_email, u.display_name FROM {$bp->groups->table_name_members} m, {$wpdb->users} u WHERE u.ID = m.user_id AND group_id = %d AND is_confirmed = 1 {$banned_sql} {$exclude_admins_sql} {$exclude_sql} ORDER BY m.date_modified DESC {$pag_sql}", $group_id ) ) ); 2790 } 2791 2792 if ( empty( $members ) ) { 2793 return false; 2794 } 2795 2796 if ( empty( $pag_sql ) ) { 2797 $total_member_count = count( $members ); 2798 } else { 2799 $total_member_count = $wpdb->get_var( apply_filters( 'bp_group_members_count_user_join_filter', $wpdb->prepare( "SELECT COUNT(user_id) FROM {$bp->groups->table_name_members} m WHERE group_id = %d AND is_confirmed = 1 {$banned_sql} {$exclude_admins_sql} {$exclude_sql}", $group_id ) ) ); 2800 } 2801 2802 // Fetch whether or not the user is a friend 2803 foreach ( (array) $members as $user ) 2804 $user_ids[] = $user->user_id; 2805 2806 $user_ids = implode( ',', wp_parse_id_list( $user_ids ) ); 2807 2808 if ( bp_is_active( 'friends' ) ) { 2809 $friend_status = $wpdb->get_results( $wpdb->prepare( "SELECT initiator_user_id, friend_user_id, is_confirmed FROM {$bp->friends->table_name} WHERE (initiator_user_id = %d AND friend_user_id IN ( {$user_ids} ) ) OR (initiator_user_id IN ( {$user_ids} ) AND friend_user_id = %d )", bp_loggedin_user_id(), bp_loggedin_user_id() ) ); 2810 for ( $i = 0, $count = count( $members ); $i < $count; ++$i ) { 2811 foreach ( (array) $friend_status as $status ) { 2812 if ( $status->initiator_user_id == $members[$i]->user_id || $status->friend_user_id == $members[$i]->user_id ) { 2813 $members[$i]->is_friend = $status->is_confirmed; 2814 } 2815 } 2816 } 2817 } 2818 2819 return array( 'members' => $members, 'count' => $total_member_count ); 2820 } 2821 2822 /** 2823 * Delete all memberships for a given group. 2824 * 2825 * @param int $group_id ID of the group. 2826 * @return int Number of records deleted. 2827 */ 2828 public static function delete_all( $group_id ) { 2829 global $wpdb; 2830 2831 $bp = buddypress(); 2832 2833 return $wpdb->query( $wpdb->prepare( "DELETE FROM {$bp->groups->table_name_members} WHERE group_id = %d", $group_id ) ); 2834 } 2835 2836 /** 2837 * Delete all group membership information for the specified user. 2838 * 2839 * @since BuddyPress (1.0.0) 2840 * 2841 * @param int $user_id ID of the user. 2842 */ 2843 public static function delete_all_for_user( $user_id ) { 2844 global $wpdb; 2845 2846 $bp = buddypress(); 2847 2848 // Get all the group ids for the current user's groups and update counts 2849 $group_ids = BP_Groups_Member::get_group_ids( $user_id ); 2850 foreach ( $group_ids['groups'] as $group_id ) { 2851 groups_update_groupmeta( $group_id, 'total_member_count', groups_get_total_member_count( $group_id ) - 1 ); 2852 2853 // If current user is the creator of a group and is the sole admin, delete that group to avoid counts going out-of-sync 2854 if ( groups_is_user_admin( $user_id, $group_id ) && count( groups_get_group_admins( $group_id ) ) < 2 && groups_is_user_creator( $user_id, $group_id ) ) 2855 groups_delete_group( $group_id ); 2856 } 2857 2858 return $wpdb->query( $wpdb->prepare( "DELETE FROM {$bp->groups->table_name_members} WHERE user_id = %d", $user_id ) ); 2859 } 2860 } 2861 2862 /** 2863 * API for creating group extensions without having to hardcode the content into 2864 * the theme. 2865 * 2866 * To implement, extend this class. In your constructor, pass an optional array 2867 * of arguments to parent::init() to configure your widget. The config array 2868 * supports the following values: 2869 * - 'slug' A unique identifier for your extension. This value will be used 2870 * to build URLs, so make it URL-safe. 2871 * - 'name' A translatable name for your extension. This value is used to 2872 * populate the navigation tab, as well as the default titles for admin/ 2873 * edit/create tabs. 2874 * - 'visibility' Set to 'public' (default) for your extension (the main tab 2875 * as well as the widget) to be available to anyone who can access the 2876 * group, 'private' otherwise. 2877 * - 'nav_item_position' An integer explaining where the nav item should 2878 * appear in the tab list. 2879 * - 'enable_nav_item' Set to true for your extension's main tab to be 2880 * available to anyone who can access the group. 2881 * - 'nav_item_name' The translatable text you want to appear in the nav tab. 2882 * Defaults to the value of 'name'. 2883 * - 'display_hook' The WordPress action that the widget_display() method is 2884 * hooked to. 2885 * - 'template_file' The template file that will be used to load the content 2886 * of your main extension tab. Defaults to 'groups/single/plugins.php'. 2887 * - 'screens' A multi-dimensional array, described below. 2888 * - 'access' Which users can visit the plugin's tab. 2889 * - 'show_tab' Which users can see the plugin's navigation tab. 2890 * 2891 * BP_Group_Extension uses the concept of "settings screens". There are three 2892 * contexts for settings screens: 2893 * - 'create', which inserts a new step into the group creation process 2894 * - 'edit', which adds a tab for your extension into the Admin section of 2895 * a group 2896 * - 'admin', which adds a metabox to the Groups administration panel in the 2897 * WordPress Dashboard 2898 * Each of these settings screens is populated by a pair of methods: one that 2899 * creates the markup for the screen, and one that processes form data 2900 * submitted from the screen. If your plugin needs screens in all three 2901 * contexts, and if the markup and form processing logic will be the same in 2902 * each case, you can define two methods to handle all of the screens: 2903 * function settings_screen() {} 2904 * function settings_screen_save() {} 2905 * If one or more of your settings screen needs separate logic, you may define 2906 * context-specific methods, for example: 2907 * function edit_screen() {} 2908 * function edit_screen_save() {} 2909 * BP_Group_Extension will use the more specific methods if they are available. 2910 * 2911 * You can further customize the settings screens (tab names, etc) by passing 2912 * an optional 'screens' parameter to the init array. The format is as follows: 2913 * 'screens' => array( 2914 * 'create' => array( 2915 * 'slug' => 'foo', 2916 * 'name' => 'Foo', 2917 * 'position' => 55, 2918 * 'screen_callback' => 'my_create_screen_callback', 2919 * 'screen_save_callback' => 'my_create_screen_save_callback', 2920 * ), 2921 * 'edit' => array( // ... 2922 * ), 2923 * Only provide those arguments that you actually want to change from the 2924 * default configuration. BP_Group_Extension will do the rest. 2925 * 2926 * Note that the 'edit' screen accepts an additional parameter: 'submit_text', 2927 * which defines the text of the Submit button automatically added to the Edit 2928 * screen of the extension (defaults to 'Save Changes'). Also, the 'admin' 2929 * screen accepts two additional parameters: 'metabox_priority' and 2930 * 'metabox_context'. See the docs for add_meta_box() for more details on these 2931 * arguments. 2932 * 2933 * Prior to BuddyPress 1.7, group extension configurations were set slightly 2934 * differently. The legacy method is still supported, though deprecated. 2935 * 2936 * @package BuddyPress 2937 * @subpackage Groups 2938 * @since BuddyPress (1.1.0) 2939 */ 2940 class BP_Group_Extension { 2941 2942 /** Public ************************************************************/ 2943 2944 /** 2945 * Information about this extension's screens. 2946 * 2947 * @since BuddyPress (1.8.0) 2948 * @access public 2949 * @var array 2950 */ 2951 public $screens = array(); 2952 2953 /** 2954 * The name of the extending class. 2955 * 2956 * @since BuddyPress (1.8.0) 2957 * @access public 2958 * @var string 2959 */ 2960 public $class_name = ''; 2961 2962 /** 2963 * A ReflectionClass object of the current extension. 2964 * 2965 * @since BuddyPress (1.8.0) 2966 * @access public 2967 * @var ReflectionClass 2968 */ 2969 public $class_reflection = null; 2970 2971 /** 2972 * Parsed configuration parameters for the extension. 2973 * 2974 * @since BuddyPress (1.8.0) 2975 * @access public 2976 * @var array 2977 */ 2978 public $params = array(); 2979 2980 /** 2981 * Raw config params, as passed by the extending class. 2982 * 2983 * @since BuddyPress (2.1.0) 2984 * @access public 2985 * @var array 2986 */ 2987 public $params_raw = array(); 2988 2989 /** 2990 * The ID of the current group. 2991 * 2992 * @since BuddyPress (1.8.0) 2993 * @access public 2994 * @var int 2995 */ 2996 public $group_id = 0; 2997 2998 /** 2999 * The slug of the current extension. 3000 * 3001 * @access public 3002 * @var string 3003 */ 3004 public $slug = ''; 3005 3006 /** 3007 * The translatable name of the current extension. 3008 * 3009 * @access public 3010 * @var string 3011 */ 3012 public $name = ''; 3013 3014 /** 3015 * The visibility of the extension tab. 'public' or 'private'. 3016 * 3017 * @access public 3018 * @var string 3019 */ 3020 public $visibility = 'public'; 3021 3022 /** 3023 * The numeric position of the main nav item. 3024 * 3025 * @access public 3026 * @var int 3027 */ 3028 public $nav_item_position = 81; 3029 3030 /** 3031 * Whether to show the nav item. 3032 * 3033 * @access public 3034 * @var bool 3035 */ 3036 public $enable_nav_item = true; 3037 3038 /** 3039 * Whether the current user should see the navigation item. 3040 * 3041 * @since BuddyPress (2.1.0) 3042 * @access public 3043 * @var bool 3044 */ 3045 public $user_can_see_nav_item; 3046 3047 /** 3048 * Whether the current user can visit the tab. 3049 * 3050 * @since BuddyPress (2.1.0) 3051 * @access public 3052 * @var bool 3053 */ 3054 public $user_can_visit; 3055 3056 /** 3057 * The text of the nav item. Defaults to self::name. 3058 * 3059 * @access public 3060 * @var string 3061 */ 3062 public $nav_item_name = ''; 3063 3064 /** 3065 * The WP action that self::widget_display() is attached to. 3066 * 3067 * Default: 'groups_custom_group_boxes'. 3068 * 3069 * @access public 3070 * @var string 3071 */ 3072 public $display_hook = 'groups_custom_group_boxes'; 3073 3074 /** 3075 * The template file used to load the plugin content. 3076 * 3077 * Default: 'groups/single/plugins'. 3078 * 3079 * @access public 3080 * @var string 3081 */ 3082 public $template_file = 'groups/single/plugins'; 3083 3084 /** Protected *********************************************************/ 3085 3086 /** 3087 * Has the extension been initialized? 3088 * 3089 * @since BuddyPress (1.8.0) 3090 * @access protected 3091 * @var bool 3092 */ 3093 protected $initialized = false; 3094 3095 /** 3096 * Extension properties as set by legacy extensions. 3097 * 3098 * @since BuddyPress (1.8.0) 3099 * @access protected 3100 * @var array 3101 */ 3102 protected $legacy_properties = array(); 3103 3104 /** 3105 * Converted legacy parameters. 3106 * 3107 * These are the extension properties as set by legacy extensions, but 3108 * then converted to match the new format for params. 3109 * 3110 * @since BuddyPress (1.8.0) 3111 * @access protected 3112 * @var array 3113 */ 3114 protected $legacy_properties_converted = array(); 3115 3116 /** 3117 * Redirect location as defined by post-edit save callback. 3118 * 3119 * @since BuddyPress (2.1.0) 3120 * @access protected 3121 * @var string 3122 */ 3123 protected $post_save_redirect; 3124 3125 /** 3126 * Miscellaneous data as set by the __set() magic method. 3127 * 3128 * @since BuddyPress (1.8.0) 3129 * @access protected 3130 * @var array 3131 */ 3132 protected $data = array(); 3133 3134 /** Screen Overrides **************************************************/ 3135 3136 /* 3137 * Screen override methods are how your extension will display content 3138 * and handle form submits. Your extension should only override those 3139 * methods that it needs for its purposes. 3140 */ 3141 3142 // The content of the group tab 3143 public function display( $group_id = null ) {} 3144 3145 // Content displayed in a widget sidebar, if applicable 3146 public function widget_display() {} 3147 3148 // *_screen() displays the settings form for the given context 3149 // *_screen_save() processes data submitted via the settings form 3150 // The settings_* methods are generic fallbacks, which can optionally 3151 // be overridden by the more specific edit_*, create_*, and admin_* 3152 // versions. 3153 public function settings_screen( $group_id = null ) {} 3154 public function settings_screen_save( $group_id = null ) {} 3155 public function edit_screen( $group_id = null ) {} 3156 public function edit_screen_save( $group_id = null ) {} 3157 public function create_screen( $group_id = null ) {} 3158 public function create_screen_save( $group_id = null ) {} 3159 public function admin_screen( $group_id = null ) {} 3160 public function admin_screen_save( $group_id = null ) {} 3161 3162 /** Setup *************************************************************/ 3163 3164 /** 3165 * Initialize the extension, using your config settings 3166 * 3167 * Your plugin should call this method at the very end of its 3168 * constructor, like so: 3169 * 3170 * public function __construct() { 3171 * $args = array( 3172 * 'slug' => 'my-group-extension', 3173 * 'name' => 'My Group Extension', 3174 * // ... 3175 * ); 3176 * 3177 * parent::init( $args ); 3178 * } 3179 * 3180 * @since BuddyPress (1.8.0) 3181 * @since BuddyPress (2.1.0) Added 'access' and 'show_tab' arguments 3182 * to $args. 3183 * @param array $args { 3184 * Array of initialization arguments. 3185 * @type string $slug Unique, URL-safe identifier for your 3186 * extension. 3187 * @type string $name Translatable name for your extension. Used to 3188 * populate navigation items. 3189 * @type string $visibility Optional. Set to 'public' for your 3190 * extension (the main tab as well as the widget) to be 3191 * available to anyone who can access the group; set to 3192 * 'private' otherwise. Default: 'public'. 3193 * @type int $nav_item_position Optional. Location of the nav item 3194 * in the tab list. Default: 81. 3195 * @type bool $enable_nav_item Optional. Whether the extension's 3196 * tab should be accessible to anyone who can view the group. 3197 * Default: true. 3198 * @type string $nav_item_name Optional. The translatable text you 3199 * want to appear in the nav tab. Default: the value of $name. 3200 * @type string $display_hook Optional. The WordPress action that 3201 * the widget_display() method is hooked to. 3202 * Default: 'groups_custom_group_boxes'. 3203 * @type string $template_file Optional. Theme-relative path to the 3204 * template file BP should use to load the content of your 3205 * main extension tab. Default: 'groups/single/plugins.php'. 3206 * @type array $screens A multi-dimensional array of configuration 3207 * information for the extension screens. See docblock of 3208 * {@link BP_Group_Extension} for more details. 3209 * @type string $access Which users can visit the plugin's tab. 3210 * Possible values: 'anyone', 'loggedin', 'member', 3211 * 'mod', 'admin' or 'noone' 3212 * ('member', 'mod', 'admin' refer to user's role in group.) 3213 * Defaults to 'anyone' for public groups and 'member' for 3214 * private groups. 3215 * @type string $show_tab Which users can see the plugin's navigation 3216 * tab. 3217 * Possible values: 'anyone', 'loggedin', 'member', 3218 * 'mod', 'admin' or 'noone' 3219 * ('member', 'mod', 'admin' refer to user's role in group.) 3220 * Defaults to 'anyone' for public groups and 'member' for 3221 * private groups. 3222 * } 3223 */ 3224 public function init( $args = array() ) { 3225 // Store the raw arguments 3226 $this->params_raw = $args; 3227 3228 // Before this init() method was introduced, plugins were 3229 // encouraged to set their config directly. For backward 3230 // compatibility with these plugins, we detect whether this is 3231 // one of those legacy plugins, and parse any legacy arguments 3232 // with those passed to init() 3233 $this->parse_legacy_properties(); 3234 $args = $this->parse_args_r( $args, $this->legacy_properties_converted ); 3235 3236 // Parse with defaults 3237 $this->params = $this->parse_args_r( $args, array( 3238 'slug' => $this->slug, 3239 'name' => $this->name, 3240 'visibility' => $this->visibility, 3241 'nav_item_position' => $this->nav_item_position, 3242 'enable_nav_item' => (bool) $this->enable_nav_item, 3243 'nav_item_name' => $this->nav_item_name, 3244 'display_hook' => $this->display_hook, 3245 'template_file' => $this->template_file, 3246 'screens' => $this->get_default_screens(), 3247 'access' => null, 3248 'show_tab' => null, 3249 ) ); 3250 3251 $this->initialized = true; 3252 } 3253 3254 /** 3255 * The main setup routine for the extension. 3256 * 3257 * This method contains the primary logic for setting up an extension's 3258 * configuration, setting up backward compatibility for legacy plugins, 3259 * and hooking the extension's screen functions into WP and BP. 3260 * 3261 * Marked 'public' because it must be accessible to add_action(). 3262 * However, you should never need to invoke this method yourself - it 3263 * is called automatically at the right point in the load order by 3264 * bp_register_group_extension(). 3265 * 3266 * @since BuddyPress (1.1.0) 3267 */ 3268 public function _register() { 3269 3270 // Detect and parse properties set by legacy extensions 3271 $this->parse_legacy_properties(); 3272 3273 // Initialize, if necessary. This should only happen for 3274 // legacy extensions that don't call parent::init() themselves 3275 if ( true !== $this->initialized ) { 3276 $this->init(); 3277 } 3278 3279 // Set some config values, based on the parsed params 3280 $this->group_id = $this->get_group_id(); 3281 $this->slug = $this->params['slug']; 3282 $this->name = $this->params['name']; 3283 $this->visibility = $this->params['visibility']; 3284 $this->nav_item_position = $this->params['nav_item_position']; 3285 $this->nav_item_name = $this->params['nav_item_name']; 3286 $this->display_hook = $this->params['display_hook']; 3287 $this->template_file = $this->params['template_file']; 3288 3289 // Configure 'screens': create, admin, and edit contexts 3290 $this->setup_screens(); 3291 3292 // Configure access-related settings 3293 $this->setup_access_settings(); 3294 3295 // Mirror configuration data so it's accessible to plugins 3296 // that look for it in its old locations 3297 $this->setup_legacy_properties(); 3298 3299 // Hook the extension into BuddyPress 3300 $this->setup_display_hooks(); 3301 $this->setup_create_hooks(); 3302 $this->setup_edit_hooks(); 3303 $this->setup_admin_hooks(); 3304 } 3305 3306 /** 3307 * Set up some basic info about the Extension. 3308 * 3309 * Here we collect the name of the extending class, as well as a 3310 * ReflectionClass that is used in get_screen_callback() to determine 3311 * whether your extension overrides certain callback methods. 3312 * 3313 * @since BuddyPress (1.8.0) 3314 */ 3315 protected function setup_class_info() { 3316 if ( empty( $this->class_name ) ) { 3317 $this->class_name = get_class( $this ); 3318 } 3319 3320 if ( is_null( $this->class_reflection ) ) { 3321 $this->class_reflection = new ReflectionClass( $this->class_name ); 3322 } 3323 } 3324 3325 /** 3326 * Get the current group ID. 3327 * 3328 * Check for: 3329 * - current group 3330 * - new group 3331 * - group admin 3332 * 3333 * @since BuddyPress (1.8.0) 3334 * 3335 * @return int 3336 */ 3337 public static function get_group_id() { 3338 3339 // Usually this will work 3340 $group_id = bp_get_current_group_id(); 3341 3342 // On the admin, get the group id out of the $_GET params 3343 if ( empty( $group_id ) && is_admin() && ( isset( $_GET['page'] ) && ( 'bp-groups' === $_GET['page'] ) ) && ! empty( $_GET['gid'] ) ) { 3344 $group_id = (int) $_GET['gid']; 3345 } 3346 3347 // This fallback will only be hit when the create step is very 3348 // early 3349 if ( empty( $group_id ) && bp_get_new_group_id() ) { 3350 $group_id = bp_get_new_group_id(); 3351 } 3352 3353 // On some setups, the group id has to be fetched out of the 3354 // $_POST array 3355 // @todo Figure out why this is happening during group creation 3356 if ( empty( $group_id ) && isset( $_POST['group_id'] ) ) { 3357 $group_id = (int) $_POST['group_id']; 3358 } 3359 3360 return $group_id; 3361 } 3362 3363 /** 3364 * Gather configuration data about your screens. 3365 * 3366 * @since BuddyPress (1.8.0) 3367 * 3368 * @return array 3369 */ 3370 protected function get_default_screens() { 3371 $this->setup_class_info(); 3372 3373 $screens = array( 3374 'create' => array( 3375 'position' => 81, 3376 ), 3377 'edit' => array( 3378 'submit_text' => __( 'Save Changes', 'buddypress' ), 3379 ), 3380 'admin' => array( 3381 'metabox_context' => 'normal', 3382 'metabox_priority' => 'core', 3383 ), 3384 ); 3385 3386 foreach ( $screens as $context => &$screen ) { 3387 $screen['enabled'] = true; 3388 $screen['name'] = $this->name; 3389 $screen['slug'] = $this->slug; 3390 3391 $screen['screen_callback'] = $this->get_screen_callback( $context, 'screen' ); 3392 $screen['screen_save_callback'] = $this->get_screen_callback( $context, 'screen_save' ); 3393 } 3394 3395 return $screens; 3396 } 3397 3398 /** 3399 * Set up screens array based on params. 3400 * 3401 * @since BuddyPress (1.8.0) 3402 */ 3403 protected function setup_screens() { 3404 foreach ( (array) $this->params['screens'] as $context => $screen ) { 3405 if ( empty( $screen['slug'] ) ) { 3406 $screen['slug'] = $this->slug; 3407 } 3408 3409 if ( empty( $screen['name'] ) ) { 3410 $screen['name'] = $this->name; 3411 } 3412 3413 $this->screens[ $context ] = $screen; 3414 } 3415 } 3416 3417 /** 3418 * Set up access-related settings for this extension. 3419 * 3420 * @since BuddyPress (2.1.0) 3421 */ 3422 protected function setup_access_settings() { 3423 // Bail if no group ID is available 3424 if ( empty( $this->group_id ) ) { 3425 return; 3426 } 3427 3428 // Backward compatibility 3429 if ( isset( $this->params['enable_nav_item'] ) ) { 3430 $this->enable_nav_item = (bool) $this->params['enable_nav_item']; 3431 } 3432 3433 // Tab Access 3434 $this->user_can_visit = false; 3435 3436 // Backward compatibility for components that do not provide 3437 // explicit 'access' parameter 3438 if ( empty( $this->params['access'] ) ) { 3439 if ( false === $this->enable_nav_item ) { 3440 $this->params['access'] = 'noone'; 3441 } else { 3442 $group = groups_get_group( array( 3443 'group_id' => $this->group_id, 3444 ) ); 3445 3446 if ( ! empty( $group->status ) && 'public' === $group->status ) { 3447 // Tabs in public groups are accessible to anyone by default 3448 $this->params['access'] = 'anyone'; 3449 } else { 3450 // All other groups have members-only as the default 3451 $this->params['access'] = 'member'; 3452 } 3453 } 3454 } 3455 3456 // Parse multiple access conditions into an array 3457 $access_conditions = $this->params['access']; 3458 if ( ! is_array( $access_conditions ) ) { 3459 $access_conditions = explode( ',', $access_conditions ); 3460 } 3461 3462 // If the current user meets at least one condition, the 3463 // get access 3464 foreach ( $access_conditions as $access_condition ) { 3465 if ( $this->user_meets_access_condition( $access_condition ) ) { 3466 $this->user_can_visit = true; 3467 break; 3468 } 3469 } 3470 3471 // Tab Visibility 3472 $this->user_can_see_nav_item = false; 3473 3474 // Backward compatibility for components that do not provide 3475 // explicit 'show_tab' parameter 3476 if ( empty( $this->params['show_tab'] ) ) { 3477 if ( false === $this->params['enable_nav_item'] ) { 3478 // enable_nav_item is only false if it's been 3479 // defined explicitly as such in the 3480 // constructor. So we always trust this value 3481 $this->params['show_tab'] = 'noone'; 3482 3483 } elseif ( isset( $this->params_raw['enable_nav_item'] ) || isset( $this->params_raw['visibility'] ) ) { 3484 // If enable_nav_item or visibility is passed, 3485 // we assume this is a legacy extension. 3486 // Legacy behavior is that enable_nav_item=true + 3487 // visibility=private implies members-only 3488 if ( 'public' !== $this->visibility ) { 3489 $this->params['show_tab'] = 'member'; 3490 } else { 3491 $this->params['show_tab'] = 'anyone'; 3492 } 3493 3494 } else { 3495 // No show_tab or enable_nav_item value is 3496 // available, so match the value of 'access' 3497 $this->params['show_tab'] = $this->params['access']; 3498 } 3499 } 3500 3501 // Parse multiple access conditions into an array 3502 $access_conditions = $this->params['show_tab']; 3503 if ( ! is_array( $access_conditions ) ) { 3504 $access_conditions = explode( ',', $access_conditions ); 3505 } 3506 3507 // If the current user meets at least one condition, the 3508 // get access 3509 foreach ( $access_conditions as $access_condition ) { 3510 if ( $this->user_meets_access_condition( $access_condition ) ) { 3511 $this->user_can_see_nav_item = true; 3512 break; 3513 } 3514 } 3515 } 3516 3517 /** 3518 * Check whether the current user meets an access condition. 3519 * 3520 * @param string $access_condition 'anyone', 'loggedin', 'member', 3521 * 'mod', 'admin' or 'noone'. 3522 * @return bool 3523 */ 3524 protected function user_meets_access_condition( $access_condition ) { 3525 $group = groups_get_group( array( 3526 'group_id' => $this->group_id, 3527 ) ); 3528 3529 switch ( $access_condition ) { 3530 case 'admin' : 3531 $meets_condition = groups_is_user_admin( bp_loggedin_user_id(), $this->group_id ); 3532 break; 3533 3534 case 'mod' : 3535 $meets_condition = groups_is_user_mod( bp_loggedin_user_id(), $this->group_id ); 3536 break; 3537 3538 case 'member' : 3539 $meets_condition = groups_is_user_member( bp_loggedin_user_id(), $this->group_id ); 3540 break; 3541 3542 case 'loggedin' : 3543 $meets_condition = is_user_logged_in(); 3544 break; 3545 3546 case 'noone' : 3547 $meets_condition = false; 3548 break; 3549 3550 case 'anyone' : 3551 default : 3552 $meets_condition = true; 3553 break; 3554 } 3555 3556 return $meets_condition; 3557 } 3558 3559 /** Display ***********************************************************/ 3560 3561 /** 3562 * Hook this extension's group tab into BuddyPress, if necessary. 3563 * 3564 * @since BuddyPress (1.8.0) 3565 */ 3566 protected function setup_display_hooks() { 3567 3568 // Bail if not a group 3569 if ( ! bp_is_group() ) { 3570 return; 3571 } 3572 3573 // Backward compatibility only 3574 if ( ( 'public' !== $this->visibility ) && ! buddypress()->groups->current_group->user_has_access ) { 3575 return; 3576 } 3577 3578 $user_can_see_nav_item = $this->user_can_see_nav_item(); 3579 3580 if ( $user_can_see_nav_item ) { 3581 $group_permalink = bp_get_group_permalink( groups_get_current_group() ); 3582 3583 bp_core_new_subnav_item( array( 3584 'name' => ! $this->nav_item_name ? $this->name : $this->nav_item_name, 3585 'slug' => $this->slug, 3586 'parent_slug' => bp_get_current_group_slug(), 3587 'parent_url' => $group_permalink, 3588 'position' => $this->nav_item_position, 3589 'item_css_id' => 'nav-' . $this->slug, 3590 'screen_function' => array( &$this, '_display_hook' ), 3591 'user_has_access' => $user_can_see_nav_item, 3592 'no_access_url' => $group_permalink, 3593 ) ); 3594 3595 // When we are viewing the extension display page, set the title and options title 3596 if ( bp_is_current_action( $this->slug ) ) { 3597 add_filter( 'bp_group_user_has_access', array( $this, 'group_access_protection' ), 10, 2 ); 3598 add_action( 'bp_template_content_header', create_function( '', 'echo "' . esc_attr( $this->name ) . '";' ) ); 3599 add_action( 'bp_template_title', create_function( '', 'echo "' . esc_attr( $this->name ) . '";' ) ); 3600 } 3601 } 3602 3603 // Hook the group home widget 3604 if ( ! bp_current_action() && bp_is_current_action( 'home' ) ) { 3605 add_action( $this->display_hook, array( &$this, 'widget_display' ) ); 3606 } 3607 } 3608 3609 /** 3610 * Hook the main display method, and loads the template file 3611 */ 3612 public function _display_hook() { 3613 add_action( 'bp_template_content', array( &$this, 'call_display' ) ); 3614 bp_core_load_template( apply_filters( 'bp_core_template_plugin', $this->template_file ) ); 3615 } 3616 3617 /** 3618 * Call the display() method. 3619 * 3620 * We use this wrapper so that we can pass the group_id to the 3621 * display() callback. 3622 * 3623 * @since BuddyPress (2.1.1) 3624 */ 3625 public function call_display() { 3626 $this->display( $this->group_id ); 3627 } 3628 3629 /** 3630 * Determine whether the current user should see this nav tab. 3631 * 3632 * Note that this controls only the display of the navigation item. 3633 * Access to the tab is controlled by the user_can_visit() check. 3634 * 3635 * @since BuddyPress (2.1.0) 3636 * 3637 * @return bool 3638 */ 3639 public function user_can_see_nav_item( $user_can_see_nav_item = false ) { 3640 if ( 'noone' !== $this->params['show_tab'] && current_user_can( 'bp_moderate' ) ) { 3641 return true; 3642 } 3643 3644 return $this->user_can_see_nav_item; 3645 } 3646 3647 /** 3648 * Determine whether the current user has access to visit this tab. 3649 * 3650 * @since BuddyPress (2.1.0) 3651 * 3652 * @return bool 3653 */ 3654 public function user_can_visit( $user_can_visit = false ) { 3655 if ( 'noone' !== $this->params['access'] && current_user_can( 'bp_moderate' ) ) { 3656 return true; 3657 } 3658 3659 return $this->user_can_visit; 3660 } 3661 3662 /** 3663 * Filter the access check in bp_groups_group_access_protection() for this extension. 3664 * 3665 * Note that $no_access_args is passed by reference, as there are some 3666 * circumstances where the bp_core_no_access() arguments need to be 3667 * modified before the redirect takes place. 3668 * 3669 * @since BuddyPress (2.1.0) 3670 * 3671 * @param bool $user_can_visit 3672 * @param array $no_access_args 3673 * @return bool 3674 */ 3675 public function group_access_protection( $user_can_visit, &$no_access_args ) { 3676 $user_can_visit = $this->user_can_visit(); 3677 3678 if ( ! $user_can_visit && is_user_logged_in() ) { 3679 $current_group = groups_get_group( array( 3680 'group_id' => $this->group_id, 3681 ) ); 3682 3683 $no_access_args['message'] = __( 'You do not have access to this content.', 'buddypress' ); 3684 $no_access_args['root'] = bp_get_group_permalink( $current_group ) . 'home/'; 3685 $no_access_args['redirect'] = false; 3686 } 3687 3688 return $user_can_visit; 3689 } 3690 3691 3692 /** Create ************************************************************/ 3693 3694 /** 3695 * Hook this extension's Create step into BuddyPress, if necessary. 3696 * 3697 * @since BuddyPress (1.8.0) 3698 */ 3699 protected function setup_create_hooks() { 3700 if ( ! $this->is_screen_enabled( 'create' ) ) { 3701 return; 3702 } 3703 3704 $screen = $this->screens['create']; 3705 3706 // Insert the group creation step for the new group extension 3707 buddypress()->groups->group_creation_steps[ $screen['slug'] ] = array( 3708 'name' => $screen['name'], 3709 'slug' => $screen['slug'], 3710 'position' => $screen['position'], 3711 ); 3712 3713 // The maybe_ methods check to see whether the create_* 3714 // callbacks should be invoked (ie, are we on the 3715 // correct group creation step). Hooked in separate 3716 // methods because current creation step info not yet 3717 // available at this point 3718 add_action( 'groups_custom_create_steps', array( $this, 'maybe_create_screen' ) ); 3719 add_action( 'groups_create_group_step_save_' . $screen['slug'], array( $this, 'maybe_create_screen_save' ) ); 3720 } 3721 3722 /** 3723 * Call the create_screen() method, if we're on the right page. 3724 * 3725 * @since BuddyPress (1.8.0) 3726 */ 3727 public function maybe_create_screen() { 3728 if ( ! bp_is_group_creation_step( $this->screens['create']['slug'] ) ) { 3729 return; 3730 } 3731 3732 call_user_func( $this->screens['create']['screen_callback'], $this->group_id ); 3733 $this->nonce_field( 'create' ); 3734 3735 // The create screen requires an additional nonce field 3736 // due to a quirk in the way the templates are built 3737 wp_nonce_field( 'groups_create_save_' . bp_get_groups_current_create_step(), '_wpnonce', false ); 3738 } 3739 3740 /** 3741 * Call the create_screen_save() method, if we're on the right page. 3742 * 3743 * @since BuddyPress (1.8.0) 3744 */ 3745 public function maybe_create_screen_save() { 3746 if ( ! bp_is_group_creation_step( $this->screens['create']['slug'] ) ) { 3747 return; 3748 } 3749 3750 $this->check_nonce( 'create' ); 3751 call_user_func( $this->screens['create']['screen_save_callback'], $this->group_id ); 3752 } 3753 3754 /** Edit **************************************************************/ 3755 3756 /** 3757 * Hook this extension's Edit panel into BuddyPress, if necessary. 3758 * 3759 * @since BuddyPress (1.8.0) 3760 */ 3761 protected function setup_edit_hooks() { 3762 // Bail if not in a group 3763 if ( ! bp_is_group() ) { 3764 return; 3765 } 3766 3767 // Bail if not an edit screen 3768 if ( ! $this->is_screen_enabled( 'edit' ) || ! bp_is_item_admin() ) { 3769 return; 3770 } 3771 3772 $screen = $this->screens['edit']; 3773 3774 $position = isset( $screen['position'] ) ? (int) $screen['position'] : 10; 3775 $position += 40; 3776 3777 $current_group = groups_get_current_group(); 3778 $admin_link = trailingslashit( bp_get_group_permalink( $current_group ) . 'admin' ); 3779 3780 $subnav_args = array( 3781 'name' => $screen['name'], 3782 'slug' => $screen['slug'], 3783 'parent_slug' => $current_group->slug . '_manage', 3784 'parent_url' => trailingslashit( bp_get_group_permalink( $current_group ) . 'admin' ), 3785 'user_has_access' => bp_is_item_admin(), 3786 'position' => $position, 3787 'screen_function' => 'groups_screen_group_admin', 3788 ); 3789 3790 // Should we add a menu to the Group's WP Admin Bar 3791 if ( ! empty( $screen['show_in_admin_bar'] ) ) { 3792 $subnav_args['show_in_admin_bar'] = true; 3793 } 3794 3795 // Add the tab to the manage navigation 3796 bp_core_new_subnav_item( $subnav_args ); 3797 3798 // Catch the edit screen and forward it to the plugin template 3799 if ( bp_is_groups_component() && bp_is_current_action( 'admin' ) && bp_is_action_variable( $screen['slug'], 0 ) ) { 3800 $this->call_edit_screen_save( $this->group_id ); 3801 3802 add_action( 'groups_custom_edit_steps', array( &$this, 'call_edit_screen' ) ); 3803 3804 // Determine the proper template and save for later 3805 // loading 3806 if ( '' !== bp_locate_template( array( 'groups/single/home.php' ), false ) ) { 3807 $this->edit_screen_template = '/groups/single/home'; 3808 } else { 3809 add_action( 'bp_template_content_header', create_function( '', 'echo "<ul class=\"content-header-nav\">"; bp_group_admin_tabs(); echo "</ul>";' ) ); 3810 add_action( 'bp_template_content', array( &$this, 'call_edit_screen' ) ); 3811 $this->edit_screen_template = '/groups/single/plugins'; 3812 } 3813 3814 // We load the template at bp_screens, to give all 3815 // extensions a chance to load 3816 add_action( 'bp_screens', array( $this, 'call_edit_screen_template_loader' ) ); 3817 } 3818 } 3819 3820 /** 3821 * Call the edit_screen() method. 3822 * 3823 * Previous versions of BP_Group_Extension required plugins to provide 3824 * their own Submit button and nonce fields when building markup. In 3825 * BP 1.8, this requirement was lifted - BP_Group_Extension now handles 3826 * all required submit buttons and nonces. 3827 * 3828 * We put the edit screen markup into an output buffer before echoing. 3829 * This is so that we can check for the presence of a hardcoded submit 3830 * button, as would be present in legacy plugins; if one is found, we 3831 * do not auto-add our own button. 3832 * 3833 * @since BuddyPress (1.8.0) 3834 */ 3835 public function call_edit_screen() { 3836 ob_start(); 3837 call_user_func( $this->screens['edit']['screen_callback'], $this->group_id ); 3838 $screen = ob_get_contents(); 3839 ob_end_clean(); 3840 3841 echo $this->maybe_add_submit_button( $screen ); 3842 3843 $this->nonce_field( 'edit' ); 3844 } 3845 3846 /** 3847 * Check the nonce, and call the edit_screen_save() method. 3848 * 3849 * @since BuddyPress (1.8.0) 3850 */ 3851 public function call_edit_screen_save() { 3852 if ( empty( $_POST ) ) { 3853 return; 3854 } 3855 3856 // When DOING_AJAX, the POST global will be populated, but we 3857 // should assume it's a save 3858 if ( defined( 'DOING_AJAX' ) && DOING_AJAX ) { 3859 return; 3860 } 3861 3862 $this->check_nonce( 'edit' ); 3863 3864 // Detect whether the screen_save_callback is performing a 3865 // redirect, so that we don't do one of our own 3866 add_filter( 'wp_redirect', array( $this, 'detect_post_save_redirect' ) ); 3867 3868 // Call the extension's save routine 3869 call_user_func( $this->screens['edit']['screen_save_callback'], $this->group_id ); 3870 3871 // Clean up detection filters 3872 remove_filter( 'wp_redirect', array( $this, 'detect_post_save_redirect' ) ); 3873 3874 // Perform a redirect only if one has not already taken place 3875 if ( empty( $this->post_save_redirect ) ) { 3876 $redirect_to = apply_filters( 'bp_group_extension_edit_screen_save_redirect', bp_get_requested_url( ) ); 3877 3878 bp_core_redirect( $redirect_to ); 3879 die(); 3880 } 3881 } 3882 3883 /** 3884 * Load the template that houses the Edit screen. 3885 * 3886 * Separated out into a callback so that it can run after all other 3887 * Group Extensions have had a chance to register their navigation, to 3888 * avoid missing tabs. 3889 * 3890 * Hooked to 'bp_screens'. 3891 * 3892 * @since BuddyPress (1.8.0) 3893 * @access public So that do_action() has access. Do not call directly. 3894 * 3895 * @see BP_Group_Extension::setup_edit_hooks() 3896 */ 3897 public function call_edit_screen_template_loader() { 3898 bp_core_load_template( $this->edit_screen_template ); 3899 } 3900 3901 /** 3902 * Add a submit button to the edit form, if it needs one. 3903 * 3904 * There's an inconsistency in the way that the group Edit and Create 3905 * screens are rendered: the Create screen has a submit button built 3906 * in, but the Edit screen does not. This function allows plugin 3907 * authors to write markup that does not contain the submit button for 3908 * use on both the Create and Edit screens - BP will provide the button 3909 * if one is not found. 3910 * 3911 * @since BuddyPress (1.8.0) 3912 * 3913 * @param string $screen The screen markup, captured in the output 3914 * buffer. 3915 * @param string $screen The same markup, with a submit button added. 3916 */ 3917 protected function maybe_add_submit_button( $screen = '' ) { 3918 if ( $this->has_submit_button( $screen ) ) { 3919 return $screen; 3920 } 3921 3922 return $screen . sprintf( 3923 '<div id="%s"><input type="submit" name="save" value="%s" id="%s"></div>', 3924 'bp-group-edit-' . $this->slug . '-submit-wrapper', 3925 $this->screens['edit']['submit_text'], 3926 'bp-group-edit-' . $this->slug . '-submit' 3927 ); 3928 } 3929 3930 /** 3931 * Does the given markup have a submit button? 3932 * 3933 * @since BuddyPress (1.8.0) 3934 * 3935 * @param string $screen The markup to check. 3936 * @return bool True if a Submit button is found, otherwise false. 3937 */ 3938 public static function has_submit_button( $screen = '' ) { 3939 $pattern = "/<input[^>]+type=[\'\"]submit[\'\"]/"; 3940 preg_match( $pattern, $screen, $matches ); 3941 return ! empty( $matches[0] ); 3942 } 3943 3944 /** 3945 * Detect redirects hardcoded into edit_screen_save() callbacks. 3946 * 3947 * @since BuddyPress (2.1.0) 3948 * 3949 * @param string $location 3950 * @return string 3951 */ 3952 public function detect_post_save_redirect( $redirect = '' ) { 3953 if ( ! empty( $redirect ) ) { 3954 $this->post_save_redirect = $redirect; 3955 } 3956 3957 return $redirect; 3958 } 3959 3960 /** Admin *************************************************************/ 3961 3962 /** 3963 * Hook this extension's Admin metabox into BuddyPress, if necessary. 3964 * 3965 * @since BuddyPress (1.8.0) 3966 */ 3967 protected function setup_admin_hooks() { 3968 if ( ! $this->is_screen_enabled( 'admin' ) || ! is_admin() ) { 3969 return; 3970 } 3971 3972 // Hook the admin screen markup function to the content hook 3973 add_action( 'bp_groups_admin_meta_box_content_' . $this->slug, array( $this, 'call_admin_screen' ) ); 3974 3975 // Initialize the metabox 3976 add_action( 'bp_groups_admin_meta_boxes', array( $this, '_meta_box_display_callback' ) ); 3977 3978 // Catch the metabox save 3979 add_action( 'bp_group_admin_edit_after', array( $this, 'call_admin_screen_save' ), 10 ); 3980 } 3981 3982 /** 3983 * Call the admin_screen() method, and add a nonce field. 3984 * 3985 * @since BuddyPress (1.8.0) 3986 */ 3987 public function call_admin_screen() { 3988 call_user_func( $this->screens['admin']['screen_callback'], $this->group_id ); 3989 $this->nonce_field( 'admin' ); 3990 } 3991 3992 /** 3993 * Check the nonce, and call the admin_screen_save() method 3994 * 3995 * @since BuddyPress (1.8.0) 3996 */ 3997 public function call_admin_screen_save() { 3998 $this->check_nonce( 'admin' ); 3999 call_user_func( $this->screens['admin']['screen_save_callback'], $this->group_id ); 4000 } 4001 4002 /** 4003 * Create the Dashboard meta box for this extension. 4004 * 4005 * @since BuddyPress (1.7.0) 4006 */ 4007 public function _meta_box_display_callback() { 4008 $group_id = isset( $_GET['gid'] ) ? (int) $_GET['gid'] : 0; 4009 $screen = $this->screens['admin']; 4010 4011 add_meta_box( 4012 $screen['slug'], 4013 $screen['name'], 4014 create_function( '', 'do_action( "bp_groups_admin_meta_box_content_' . $this->slug . '", ' . $group_id . ' );' ), 4015 get_current_screen()->id, 4016 $screen['metabox_context'], 4017 $screen['metabox_priority'] 4018 ); 4019 } 4020 4021 4022 /** Utilities *********************************************************/ 4023 4024 /** 4025 * Generate the nonce fields for a settings form. 4026 * 4027 * The nonce field name (the second param passed to wp_nonce_field) 4028 * contains this extension's slug and is thus unique to this extension. 4029 * This is necessary because in some cases (namely, the Dashboard), 4030 * more than one extension may generate nonces on the same page, and we 4031 * must avoid name clashes. 4032 * 4033 * @since BuddyPress (1.8.0) 4034 * 4035 * @param string $context Screen context. 'create', 'edit', or 'admin'. 4036 */ 4037 public function nonce_field( $context = '' ) { 4038 wp_nonce_field( 'bp_group_extension_' . $this->slug . '_' . $context, '_bp_group_' . $context . '_nonce_' . $this->slug ); 4039 } 4040 4041 /** 4042 * Check the nonce on a submitted settings form. 4043 * 4044 * @since BuddyPress (1.8.0) 4045 * 4046 * @param string $context Screen context. 'create', 'edit', or 'admin'. 4047 */ 4048 public function check_nonce( $context = '' ) { 4049 check_admin_referer( 'bp_group_extension_' . $this->slug . '_' . $context, '_bp_group_' . $context . '_nonce_' . $this->slug ); 4050 } 4051 4052 /** 4053 * Is the specified screen enabled? 4054 * 4055 * To be enabled, a screen must both have the 'enabled' key set to true 4056 * (legacy: $this->enable_create_step, etc), and its screen_callback 4057 * must also exist and be callable. 4058 * 4059 * @since BuddyPress (1.8.0) 4060 * 4061 * @param string $context Screen context. 'create', 'edit', or 'admin'. 4062 * @return bool True if the screen is enabled, otherwise false. 4063 */ 4064 public function is_screen_enabled( $context = '' ) { 4065 $enabled = false; 4066 4067 if ( isset( $this->screens[ $context ] ) ) { 4068 $enabled = $this->screens[ $context ]['enabled'] && is_callable( $this->screens[ $context ]['screen_callback'] ); 4069 } 4070 4071 return (bool) $enabled; 4072 } 4073 4074 /** 4075 * Get the appropriate screen callback for the specified context/type. 4076 * 4077 * BP Group Extensions have three special "screen contexts": create, 4078 * admin, and edit. Each of these contexts has a corresponding 4079 * _screen() and _screen_save() method, which allow group extension 4080 * plugins to define different markup and logic for each context. 4081 * 4082 * BP also supports fallback settings_screen() and 4083 * settings_screen_save() methods, which can be used to define markup 4084 * and logic that is shared between context. For each context, you may 4085 * either provide context-specific methods, or you can let BP fall back 4086 * on the shared settings_* callbacks. 4087 * 4088 * For example, consider a BP_Group_Extension implementation that looks 4089 * like this: 4090 * 4091 * // ... 4092 * function create_screen( $group_id ) { ... } 4093 * function create_screen_save( $group_id ) { ... } 4094 * function settings_screen( $group_id ) { ... } 4095 * function settings_screen_save( $group_id ) { ... } 4096 * // ... 4097 * 4098 * BP_Group_Extension will use your create_* methods for the Create 4099 * steps, and will use your generic settings_* methods for the Edit 4100 * and Admin contexts. This schema allows plugin authors maximum 4101 * flexibility without having to repeat themselves. 4102 * 4103 * The get_screen_callback() method uses a ReflectionClass object to 4104 * determine whether your extension has provided a given callback. 4105 * 4106 * @since BuddyPress (1.8.0) 4107 * 4108 * @param string $context Screen context. 'create', 'edit', or 'admin'. 4109 * @param string $type Screen type. 'screen' or 'screen_save'. Default: 4110 * 'screen'. 4111 * @return callable A callable function handle. 4112 */ 4113 public function get_screen_callback( $context = '', $type = 'screen' ) { 4114 $callback = ''; 4115 4116 // Try the context-specific callback first 4117 $method = $context . '_' . $type; 4118 $rmethod = $this->class_reflection->getMethod( $method ); 4119 if ( isset( $rmethod->class ) && $this->class_name === $rmethod->class ) { 4120 $callback = array( $this, $method ); 4121 } 4122 4123 if ( empty( $callback ) ) { 4124 $fallback_method = 'settings_' . $type; 4125 $rfallback_method = $this->class_reflection->getMethod( $fallback_method ); 4126 if ( isset( $rfallback_method->class ) && $this->class_name === $rfallback_method->class ) { 4127 $callback = array( $this, $fallback_method ); 4128 } 4129 } 4130 4131 return $callback; 4132 } 4133 4134 /** 4135 * Recursive argument parsing. 4136 * 4137 * This acts like a multi-dimensional version of wp_parse_args() (minus 4138 * the querystring parsing - you must pass arrays). 4139 * 4140 * Values from $a override those from $b; keys in $b that don't exist 4141 * in $a are passed through. 4142 * 4143 * This is different from array_merge_recursive(), both because of the 4144 * order of preference ($a overrides $b) and because of the fact that 4145 * array_merge_recursive() combines arrays deep in the tree, rather 4146 * than overwriting the b array with the a array. 4147 * 4148 * The implementation of this function is specific to the needs of 4149 * BP_Group_Extension, where we know that arrays will always be 4150 * associative, and that an argument under a given key in one array 4151 * will be matched by a value of identical depth in the other one. The 4152 * function is NOT designed for general use, and will probably result 4153 * in unexpected results when used with data in the wild. See, eg, 4154 * http://core.trac.wordpress.org/ticket/19888 4155 * 4156 * @since BuddyPress (1.8.0) 4157 * 4158 * @param array $a First set of arguments. 4159 * @param array $b Second set of arguments. 4160 * @return array Parsed arguments. 4161 */ 4162 public static function parse_args_r( &$a, $b ) { 4163 $a = (array) $a; 4164 $b = (array) $b; 4165 $r = $b; 4166 4167 foreach ( $a as $k => &$v ) { 4168 if ( is_array( $v ) && isset( $r[ $k ] ) ) { 4169 $r[ $k ] = self::parse_args_r( $v, $r[ $k ] ); 4170 } else { 4171 $r[ $k ] = $v; 4172 } 4173 } 4174 4175 return $r; 4176 } 4177 4178 /** Legacy Support ********************************************************/ 4179 4180 /* 4181 * In BuddyPress 1.8, the recommended technique for configuring 4182 * extensions changed from directly setting various object properties 4183 * in the class constructor, to passing a configuration array to 4184 * parent::init(). The following methods ensure that extensions created 4185 * in the old way continue to work, by converting legacy configuration 4186 * data to the new format. 4187 */ 4188 4189 /** 4190 * Provide access to otherwise unavailable object properties. 4191 * 4192 * This magic method is here for backward compatibility with plugins 4193 * that refer to config properties that have moved to a different 4194 * location (such as enable_create_step, which is now at 4195 * $this->screens['create']['enabled'] 4196 * 4197 * The legacy_properties array is set up in 4198 * self::setup_legacy_properties(). 4199 * 4200 * @since BuddyPress (1.8.0) 4201 * 4202 * @param string $key Property name. 4203 * @return mixed The value if found, otherwise null. 4204 */ 4205 public function __get( $key ) { 4206 if ( isset( $this->legacy_properties[ $key ] ) ) { 4207 return $this->legacy_properties[ $key ]; 4208 } elseif ( isset( $this->data[ $key ] ) ) { 4209 return $this->data[ $key ]; 4210 } else { 4211 return null; 4212 } 4213 } 4214 4215 /** 4216 * Provide a fallback for isset( $this->foo ) when foo is unavailable. 4217 * 4218 * This magic method is here for backward compatibility with plugins 4219 * that have set their class config options directly in the class 4220 * constructor. The parse_legacy_properties() method of the current 4221 * class needs to check whether any legacy keys have been put into the 4222 * $this->data array. 4223 * 4224 * @since BuddyPress (1.8.0) 4225 * 4226 * @param string $key Property name. 4227 * @return bool True if the value is set, otherwise false. 4228 */ 4229 public function __isset( $key ) { 4230 if ( isset( $this->legacy_properties[ $key ] ) ) { 4231 return true; 4232 } elseif ( isset( $this->data[ $key ] ) ) { 4233 return true; 4234 } else { 4235 return false; 4236 } 4237 } 4238 4239 /** 4240 * Allow plugins to set otherwise unavailable object properties. 4241 * 4242 * This magic method is here for backward compatibility with plugins 4243 * that may attempt to modify the group extension by manually assigning 4244 * a value to an object property that no longer exists, such as 4245 * $this->enable_create_step. 4246 * 4247 * @since BuddyPress (1.8.0) 4248 * 4249 * @param string $key Property name. 4250 * @param mixed $value Property value. 4251 */ 4252 public function __set( $key, $value ) { 4253 4254 if ( empty( $this->initialized ) ) { 4255 $this->data[ $key ] = $value; 4256 } 4257 4258 switch ( $key ) { 4259 case 'enable_create_step' : 4260 $this->screens['create']['enabled'] = $value; 4261 break; 4262 4263 case 'enable_edit_item' : 4264 $this->screens['edit']['enabled'] = $value; 4265 break; 4266 4267 case 'enable_admin_item' : 4268 $this->screens['admin']['enabled'] = $value; 4269 break; 4270 4271 case 'create_step_position' : 4272 $this->screens['create']['position'] = $value; 4273 break; 4274 4275 // Note: 'admin' becomes 'edit' to distinguish from Dashboard 'admin' 4276 case 'admin_name' : 4277 $this->screens['edit']['name'] = $value; 4278 break; 4279 4280 case 'admin_slug' : 4281 $this->screens['edit']['slug'] = $value; 4282 break; 4283 4284 case 'create_name' : 4285 $this->screens['create']['name'] = $value; 4286 break; 4287 4288 case 'create_slug' : 4289 $this->screens['create']['slug'] = $value; 4290 break; 4291 4292 case 'admin_metabox_context' : 4293 $this->screens['admin']['metabox_context'] = $value; 4294 break; 4295 4296 case 'admin_metabox_priority' : 4297 $this->screens['admin']['metabox_priority'] = $value; 4298 break; 4299 4300 default : 4301 $this->data[ $key ] = $value; 4302 break; 4303 } 4304 } 4305 4306 /** 4307 * Return a list of legacy properties. 4308 * 4309 * The legacy implementation of BP_Group_Extension used all of these 4310 * object properties for configuration. Some have been moved. 4311 * 4312 * @since BuddyPress (1.8.0) 4313 * 4314 * @return array List of legacy property keys. 4315 */ 4316 protected function get_legacy_property_list() { 4317 return array( 4318 'name', 4319 'slug', 4320 'admin_name', 4321 'admin_slug', 4322 'create_name', 4323 'create_slug', 4324 'visibility', 4325 'create_step_position', 4326 'nav_item_position', 4327 'admin_metabox_context', 4328 'admin_metabox_priority', 4329 'enable_create_step', 4330 'enable_nav_item', 4331 'enable_edit_item', 4332 'enable_admin_item', 4333 'nav_item_name', 4334 'display_hook', 4335 'template_file', 4336 ); 4337 } 4338 4339 /** 4340 * Parse legacy properties. 4341 * 4342 * The old standard for BP_Group_Extension was for plugins to register 4343 * their settings as properties in their constructor. The new method is 4344 * to pass a config array to the init() method. In order to support 4345 * legacy plugins, we slurp up legacy properties, and later on we'll 4346 * parse them into the new init() array. 4347 * 4348 * @since BuddyPress (1.8.0) 4349 */ 4350 protected function parse_legacy_properties() { 4351 4352 // Only run this one time 4353 if ( ! empty( $this->legacy_properties_converted ) ) { 4354 return; 4355 } 4356 4357 $properties = $this->get_legacy_property_list(); 4358 4359 // By-reference variable for convenience 4360 $lpc =& $this->legacy_properties_converted; 4361 4362 foreach ( $properties as $property ) { 4363 4364 // No legacy config exists for this key 4365 if ( ! isset( $this->{$property} ) ) { 4366 continue; 4367 } 4368 4369 // Grab the value and record it as appropriate 4370 $value = $this->{$property}; 4371 4372 switch ( $property ) { 4373 case 'enable_create_step' : 4374 $lpc['screens']['create']['enabled'] = (bool) $value; 4375 break; 4376 4377 case 'enable_edit_item' : 4378 $lpc['screens']['edit']['enabled'] = (bool) $value; 4379 break; 4380 4381 case 'enable_admin_item' : 4382 $lpc['screens']['admin']['enabled'] = (bool) $value; 4383 break; 4384 4385 case 'create_step_position' : 4386 $lpc['screens']['create']['position'] = $value; 4387 break; 4388 4389 // Note: 'admin' becomes 'edit' to distinguish from Dashboard 'admin' 4390 case 'admin_name' : 4391 $lpc['screens']['edit']['name'] = $value; 4392 break; 4393 4394 case 'admin_slug' : 4395 $lpc['screens']['edit']['slug'] = $value; 4396 break; 4397 4398 case 'create_name' : 4399 $lpc['screens']['create']['name'] = $value; 4400 break; 4401 4402 case 'create_slug' : 4403 $lpc['screens']['create']['slug'] = $value; 4404 break; 4405 4406 case 'admin_metabox_context' : 4407 $lpc['screens']['admin']['metabox_context'] = $value; 4408 break; 4409 4410 case 'admin_metabox_priority' : 4411 $lpc['screens']['admin']['metabox_priority'] = $value; 4412 break; 4413 4414 default : 4415 $lpc[ $property ] = $value; 4416 break; 4417 } 4418 } 4419 } 4420 4421 /** 4422 * Set up legacy properties. 4423 * 4424 * This method is responsible for ensuring that all legacy config 4425 * properties are stored in an array $this->legacy_properties, so that 4426 * they remain available to plugins that reference the variables at 4427 * their old locations. 4428 * 4429 * @since BuddyPress (1.8.0) 4430 * 4431 * @see BP_Group_Extension::__get() 4432 */ 4433 protected function setup_legacy_properties() { 4434 4435 // Only run this one time 4436 if ( ! empty( $this->legacy_properties ) ) { 4437 return; 4438 } 4439 4440 $properties = $this->get_legacy_property_list(); 4441 $params = $this->params; 4442 $lp =& $this->legacy_properties; 4443 4444 foreach ( $properties as $property ) { 4445 switch ( $property ) { 4446 case 'enable_create_step' : 4447 $lp['enable_create_step'] = $params['screens']['create']['enabled']; 4448 break; 4449 4450 case 'enable_edit_item' : 4451 $lp['enable_edit_item'] = $params['screens']['edit']['enabled']; 4452 break; 4453 4454 case 'enable_admin_item' : 4455 $lp['enable_admin_item'] = $params['screens']['admin']['enabled']; 4456 break; 4457 4458 case 'create_step_position' : 4459 $lp['create_step_position'] = $params['screens']['create']['position']; 4460 break; 4461 4462 // Note: 'admin' becomes 'edit' to distinguish from Dashboard 'admin' 4463 case 'admin_name' : 4464 $lp['admin_name'] = $params['screens']['edit']['name']; 4465 break; 4466 4467 case 'admin_slug' : 4468 $lp['admin_slug'] = $params['screens']['edit']['slug']; 4469 break; 4470 4471 case 'create_name' : 4472 $lp['create_name'] = $params['screens']['create']['name']; 4473 break; 4474 4475 case 'create_slug' : 4476 $lp['create_slug'] = $params['screens']['create']['slug']; 4477 break; 4478 4479 case 'admin_metabox_context' : 4480 $lp['admin_metabox_context'] = $params['screens']['admin']['metabox_context']; 4481 break; 4482 4483 case 'admin_metabox_priority' : 4484 $lp['admin_metabox_priority'] = $params['screens']['admin']['metabox_priority']; 4485 break; 4486 4487 default : 4488 // All other items get moved over 4489 $lp[ $property ] = $params[ $property ]; 4490 4491 // Also reapply to the object, for backpat 4492 $this->{$property} = $params[ $property ]; 4493 4494 break; 4495 } 4496 } 4497 } 4498 } 4499 4500 /** 4501 * Register a new Group Extension. 4502 * 4503 * @param string Name of the Extension class. 4504 * @return bool|null Returns false on failure, otherwise null. 4505 */ 4506 function bp_register_group_extension( $group_extension_class = '' ) { 4507 4508 if ( ! class_exists( $group_extension_class ) ) { 4509 return false; 4510 } 4511 4512 // Register the group extension on the bp_init action so we have access 4513 // to all plugins. 4514 add_action( 'bp_init', create_function( '', ' 4515 $extension = new ' . $group_extension_class . '; 4516 add_action( "bp_actions", array( &$extension, "_register" ), 8 ); 4517 add_action( "admin_init", array( &$extension, "_register" ) ); 4518 ' ), 11 ); 4519 } 4520 4521 /** 4522 * Adds support for user at-mentions (for users in a specific Group) to the Suggestions API. 4523 * 4524 * @since BuddyPress (2.1.0) 4525 */ 4526 class BP_Groups_Member_Suggestions extends BP_Members_Suggestions { 4527 4528 /** 4529 * Default arguments for this suggestions service. 4530 * 4531 * @since BuddyPress (2.1.0) 4532 * @access protected 4533 * @var array $args { 4534 * @type int $group_id Positive integers will restrict the search to members in that group. 4535 * Negative integers will restrict the search to members in every other group. 4536 * @type int $limit Maximum number of results to display. Default: 16. 4537 * @type bool $only_friends If true, only match the current user's friends. Default: false. 4538 * @type string $term The suggestion service will try to find results that contain this string. 4539 * Mandatory. 4540 * } 4541 */ 4542 protected $default_args = array( 4543 'group_id' => 0, 4544 'limit' => 16, 4545 'only_friends' => false, 4546 'term' => '', 4547 'type' => '', 4548 ); 4549 4550 4551 /** 4552 * Validate and sanitise the parameters for the suggestion service query. 4553 * 4554 * @return true|WP_Error If validation fails, return a WP_Error object. On success, return true (bool). 4555 * @since BuddyPress (2.1.0) 4556 */ 4557 public function validate() { 4558 $this->args['group_id'] = (int) $this->args['group_id']; 4559 $this->args = apply_filters( 'bp_groups_member_suggestions_args', $this->args, $this ); 4560 4561 // Check for invalid or missing mandatory parameters. 4562 if ( ! $this->args['group_id'] || ! bp_is_active( 'groups' ) ) { 4563 return new WP_Error( 'missing_requirement' ); 4564 } 4565 4566 // Check that the specified group_id exists, and that the current user can access it. 4567 $the_group = groups_get_group( array( 4568 'group_id' => absint( $this->args['group_id'] ), 4569 'populate_extras' => true, 4570 ) ); 4571 4572 if ( $the_group->id === 0 || ! $the_group->user_has_access ) { 4573 return new WP_Error( 'access_denied' ); 4574 } 4575 4576 return apply_filters( 'bp_groups_member_suggestions_validate_args', parent::validate(), $this ); 4577 } 4578 4579 /** 4580 * Find and return a list of username suggestions that match the query. 4581 * 4582 * @return array|WP_Error Array of results. If there were problems, returns a WP_Error object. 4583 * @since BuddyPress (2.1.0) 4584 */ 4585 public function get_suggestions() { 4586 $user_query = array( 4587 'count_total' => '', // Prevents total count 4588 'populate_extras' => false, 4589 'type' => 'alphabetical', 4590 4591 'group_role' => array( 'admin', 'member', 'mod' ), 4592 'page' => 1, 4593 'per_page' => $this->args['limit'], 4594 'search_terms' => $this->args['term'], 4595 'search_wildcard' => 'right', 4596 ); 4597 4598 // Only return matches of friends of this user. 4599 if ( $this->args['only_friends'] && is_user_logged_in() ) { 4600 $user_query['user_id'] = get_current_user_id(); 4601 } 4602 4603 // Positive Group IDs will restrict the search to members in that group. 4604 if ( $this->args['group_id'] > 0 ) { 4605 $user_query['group_id'] = $this->args['group_id']; 4606 4607 // Negative Group IDs will restrict the search to members in every other group. 4608 } else { 4609 $group_query = array( 4610 'count_total' => '', // Prevents total count 4611 'populate_extras' => false, 4612 'type' => 'alphabetical', 4613 4614 'group_id' => absint( $this->args['group_id'] ), 4615 'group_role' => array( 'admin', 'member', 'mod' ), 4616 'page' => 1, 4617 ); 4618 $group_users = new BP_Group_Member_Query( $group_query ); 4619 4620 if ( $group_users->results ) { 4621 $user_query['exclude'] = wp_list_pluck( $group_users->results, 'ID' ); 4622 } else { 4623 $user_query['include'] = array( 0 ); 4624 } 4625 } 4626 4627 $user_query = apply_filters( 'bp_groups_member_suggestions_query_args', $user_query, $this ); 4628 if ( is_wp_error( $user_query ) ) { 4629 return $user_query; 4630 } 4631 4632 4633 if ( isset( $user_query['group_id'] ) ) { 4634 $user_query = new BP_Group_Member_Query( $user_query ); 4635 } else { 4636 $user_query = new BP_User_Query( $user_query ); 4637 } 4638 4639 $results = array(); 4640 foreach ( $user_query->results as $user ) { 4641 $result = new stdClass(); 4642 $result->ID = $user->user_nicename; 4643 $result->image = bp_core_fetch_avatar( array( 'html' => false, 'item_id' => $user->ID ) ); 4644 $result->name = bp_core_get_user_displayname( $user->ID ); 4645 4646 $results[] = $result; 4647 } 4648 4649 return apply_filters( 'bp_groups_member_suggestions_get_suggestions', $results, $this ); 4650 } 4651 } 12 require __DIR__ . '/classes/class-bp-group-extension.php'; 13 require __DIR__ . '/classes/class-bp-group-member-query.php'; 14 require __DIR__ . '/classes/class-bp-groups-group.php'; 15 require __DIR__ . '/classes/class-bp-groups-member-suggestions.php'; 16 require __DIR__ . '/classes/class-bp-groups-member.php';
Note:
See TracChangeset
for help on using the changeset viewer.
![(please configure the [header_logo] section in trac.ini)](/chrome/site/your_project_logo.png)