[ Index ]

PHP Cross Reference of BuddyPress

title

Body

[close]

/src/bp-members/ -> bp-members-functions.php (source)

   1  <?php
   2  /**
   3   * BuddyPress Member Functions.
   4   *
   5   * Functions specific to the members component.
   6   *
   7   * @package BuddyPress
   8   * @subpackage MembersFunctions
   9   * @since 1.5.0
  10   */
  11  
  12  // Exit if accessed directly.
  13  defined( 'ABSPATH' ) || exit;
  14  
  15  /**
  16   * Check for the existence of a Members directory page.
  17   *
  18   * @since 1.5.0
  19   *
  20   * @return bool True if found, otherwise false.
  21   */
  22  function bp_members_has_directory() {
  23      $bp = buddypress();
  24  
  25      return (bool) ! empty( $bp->pages->members->id );
  26  }
  27  
  28  /**
  29   * Define the slug constants for the Members component.
  30   *
  31   * Handles the three slug constants used in the Members component -
  32   * BP_MEMBERS_SLUG, BP_REGISTER_SLUG, and BP_ACTIVATION_SLUG. If these
  33   * constants are not overridden in wp-config.php or bp-custom.php, they are
  34   * defined here to match the slug of the corresponding WP pages.
  35   *
  36   * In general, fallback values are only used during initial BP page creation,
  37   * when no slugs have been explicitly defined.
  38   *
  39   * @since 1.5.0
  40   *
  41   * @global BuddyPress $bp The one true BuddyPress instance.
  42   */
  43  function bp_core_define_slugs() {
  44      $bp = buddypress();
  45  
  46      // No custom members slug.
  47      if ( !defined( 'BP_MEMBERS_SLUG' ) ) {
  48          if ( !empty( $bp->pages->members ) ) {
  49              define( 'BP_MEMBERS_SLUG', $bp->pages->members->slug );
  50          } else {
  51              define( 'BP_MEMBERS_SLUG', 'members' );
  52          }
  53      }
  54  
  55      // No custom registration slug.
  56      if ( !defined( 'BP_REGISTER_SLUG' ) ) {
  57          if ( !empty( $bp->pages->register ) ) {
  58              define( 'BP_REGISTER_SLUG', $bp->pages->register->slug );
  59          } else {
  60              define( 'BP_REGISTER_SLUG', 'register' );
  61          }
  62      }
  63  
  64      // No custom activation slug.
  65      if ( !defined( 'BP_ACTIVATION_SLUG' ) ) {
  66          if ( !empty( $bp->pages->activate ) ) {
  67              define( 'BP_ACTIVATION_SLUG', $bp->pages->activate->slug );
  68          } else {
  69              define( 'BP_ACTIVATION_SLUG', 'activate' );
  70          }
  71      }
  72  }
  73  add_action( 'bp_setup_globals', 'bp_core_define_slugs', 11 );
  74  
  75  /**
  76   * Fetch an array of users based on the parameters passed.
  77   *
  78   * Since BuddyPress 1.7, bp_core_get_users() uses BP_User_Query. If you
  79   * need backward compatibility with BP_Core_User::get_users(), filter the
  80   * bp_use_legacy_user_query value, returning true.
  81   *
  82   * @since 1.2.0
  83   * @since 7.0.0 Added `xprofile_query` parameter. Added `user_ids` parameter.
  84   *
  85   * @param array|string $args {
  86   *     Array of arguments. All are optional. See {@link BP_User_Query} for
  87   *     a more complete description of arguments.
  88   *     @type string       $type                Sort order. Default: 'active'.
  89   *     @type int          $user_id             Limit results to friends of a user. Default: false.
  90   *     @type mixed        $exclude             IDs to exclude from results. Default: false.
  91   *     @type string       $search_terms        Limit to users matching search terms. Default: false.
  92   *     @type string       $meta_key            Limit to users with a meta_key. Default: false.
  93   *     @type string       $meta_value          Limit to users with a meta_value (with meta_key). Default: false.
  94   *     @type array|string $member_type         Array or comma-separated string of member types.
  95   *     @type array|string $member_type__in     Array or comma-separated string of member types.
  96   *                                             `$member_type` takes precedence over this parameter.
  97   *     @type array|string $member_type__not_in Array or comma-separated string of member types to be excluded.
  98   *     @type mixed        $include             Limit results by user IDs. Default: false.
  99   *     @type mixed        $user_ids            IDs corresponding to the users. Default: false.
 100   *     @type int          $per_page            Results per page. Default: 20.
 101   *     @type int          $page                Page of results. Default: 1.
 102   *     @type bool         $populate_extras     Fetch optional extras. Default: true.
 103   *     @type array        $xprofile_query      Filter results by xprofile data. Requires the xprofile
 104   *                                             component. See {@see BP_XProfile_Query} for details.
 105   *     @type string|bool  $count_total         How to do total user count. Default: 'count_query'.
 106   * }
 107   * @return array
 108   */
 109  function bp_core_get_users( $args = '' ) {
 110  
 111      // Parse the user query arguments.
 112      $r = bp_parse_args(
 113          $args,
 114          array(
 115              'type'                => 'active',     // Active, newest, alphabetical, random or popular.
 116              'user_id'             => false,        // Pass a user_id to limit to only friend connections for this user.
 117              'exclude'             => false,        // Users to exclude from results.
 118              'search_terms'        => false,        // Limit to users that match these search terms.
 119              'meta_key'            => false,        // Limit to users who have this piece of usermeta.
 120              'meta_value'          => false,        // With meta_key, limit to users where usermeta matches this value.
 121              'member_type'         => '',
 122              'member_type__in'     => '',
 123              'member_type__not_in' => '',
 124              'include'             => false,        // Pass comma separated list of user_ids to limit to only these users.
 125              'user_ids'            => false,
 126              'per_page'            => 20,           // The number of results to return per page.
 127              'page'                => 1,            // The page to return if limiting per page.
 128              'populate_extras'     => true,         // Fetch the last active, where the user is a friend, total friend count, latest update.
 129              'xprofile_query'      => false,
 130              'count_total'         => 'count_query', // What kind of total user count to do, if any. 'count_query', 'sql_calc_found_rows', or false.
 131          ),
 132          'core_get_users'
 133      );
 134  
 135      /**
 136       * For legacy users. Use of BP_Core_User::get_users() is deprecated.
 137       *
 138       * Forcing this filter to true will use the legacy user query. As of
 139       * BuddyPress 7.0.0, mirroring of the 'last_activity' value to usermeta
 140       * is also disabled if true. See bp_update_user_last_activity().
 141       *
 142       * @since 2.0.0
 143       *
 144       * @param bool   $retval   Defaults to false.
 145       * @param string $function Current function name.
 146       * @param array  $r        User query arguments.
 147       */
 148      $use_legacy_query = apply_filters( 'bp_use_legacy_user_query', false, __FUNCTION__, $r );
 149  
 150      if ( $use_legacy_query ) {
 151          $retval = BP_Core_User::get_users(
 152              $r['type'],
 153              $r['per_page'],
 154              $r['page'],
 155              $r['user_id'],
 156              $r['include'],
 157              $r['search_terms'],
 158              $r['populate_extras'],
 159              $r['exclude'],
 160              $r['meta_key'],
 161              $r['meta_value']
 162          );
 163  
 164      // Default behavior as of BuddyPress 1.7.0.
 165      } else {
 166  
 167          // Get users like we were asked to do...
 168          $users = new BP_User_Query( $r );
 169  
 170          // ...but reformat the results to match bp_core_get_users() behavior.
 171          $retval = array(
 172              'users' => array_values( $users->results ),
 173              'total' => $users->total_users
 174          );
 175      }
 176  
 177      /**
 178       * Filters the results of the user query.
 179       *
 180       * @since 1.2.0
 181       *
 182       * @param array $retval Array of users for the current query.
 183       * @param array $r      Array of parsed query arguments.
 184       */
 185      return apply_filters( 'bp_core_get_users', $retval, $r );
 186  }
 187  
 188  /**
 189   * Return the domain for the passed user: e.g. http://example.com/members/andy/.
 190   *
 191   * @since 1.0.0
 192   *
 193   * @param int         $user_id       The ID of the user.
 194   * @param string|bool $user_nicename Optional. user_nicename of the user.
 195   * @param string|bool $user_login    Optional. user_login of the user.
 196   * @return string
 197   */
 198  function bp_core_get_user_domain( $user_id = 0, $user_nicename = false, $user_login = false ) {
 199  
 200      if ( empty( $user_id ) ) {
 201          return;
 202      }
 203  
 204      $username = bp_core_get_username( $user_id, $user_nicename, $user_login );
 205  
 206      if ( bp_is_username_compatibility_mode() ) {
 207          $username = rawurlencode( $username );
 208      }
 209  
 210      $after_domain = bp_core_enable_root_profiles() ? $username : bp_get_members_root_slug() . '/' . $username;
 211      $domain       = trailingslashit( bp_get_root_domain() . '/' . $after_domain );
 212  
 213      // Don't use this filter.  Subject to removal in a future release.
 214      // Use the 'bp_core_get_user_domain' filter instead.
 215      $domain = apply_filters( 'bp_core_get_user_domain_pre_cache', $domain, $user_id, $user_nicename, $user_login );
 216  
 217      /**
 218       * Filters the domain for the passed user.
 219       *
 220       * @since 1.0.1
 221       *
 222       * @param string $domain        Domain for the passed user.
 223       * @param int    $user_id       ID of the passed user.
 224       * @param string $user_nicename User nicename of the passed user.
 225       * @param string $user_login    User login of the passed user.
 226       */
 227      return apply_filters( 'bp_core_get_user_domain', $domain, $user_id, $user_nicename, $user_login );
 228  }
 229  
 230  /**
 231   * Fetch everything in the wp_users table for a user, without any usermeta.
 232   *
 233   * @since 1.2.0
 234   *
 235   * @param int $user_id The ID of the user.
 236   * @return array|bool Array of data on success, false on failure.
 237   */
 238  function bp_core_get_core_userdata( $user_id = 0 ) {
 239      if ( empty( $user_id ) ) {
 240          return false;
 241      }
 242  
 243      // Get core user data.
 244      $userdata = BP_Core_User::get_core_userdata( $user_id );
 245  
 246      /**
 247       * Filters the userdata for a passed user.
 248       *
 249       * @since 1.2.0
 250       *
 251       * @param array|bool $userdata Array of user data for a passed user on success, false on failure.
 252       */
 253      return apply_filters( 'bp_core_get_core_userdata', $userdata );
 254  }
 255  
 256  /**
 257   * Return the ID of a user, based on user_login.
 258   *
 259   * No longer used.
 260   *
 261   * @todo Deprecate.
 262   *
 263   * @param string $user_login user_login of the user being queried.
 264   * @return int
 265   */
 266  function bp_core_get_displayed_userid( $user_login ) {
 267      return apply_filters( 'bp_core_get_displayed_userid', bp_core_get_userid( $user_login ) );
 268  }
 269  
 270  /**
 271   * Return the user ID based on a user's user_login.
 272   *
 273   * @since 1.0.0
 274   *
 275   * @param string $username user_login to check.
 276   * @return int|null The ID of the matched user on success, null on failure.
 277   */
 278  function bp_core_get_userid( $username = '' ) {
 279      if ( empty( $username ) ) {
 280          return false;
 281      }
 282  
 283      $user = get_user_by( 'login', $username );
 284  
 285      /**
 286       * Filters the ID of a user, based on user_login.
 287       *
 288       * @since 1.0.1
 289       *
 290       * @param int|null $value    ID of the user or null.
 291       * @param string   $username User login to check.
 292       */
 293      return apply_filters( 'bp_core_get_userid', ! empty( $user->ID ) ? $user->ID : null, $username );
 294  }
 295  
 296  /**
 297   * Return the user ID based on a user's user_nicename.
 298   *
 299   * @since 1.2.3
 300   *
 301   * @param string $user_nicename user_nicename to check.
 302   * @return int|null The ID of the matched user on success, null on failure.
 303   */
 304  function bp_core_get_userid_from_nicename( $user_nicename = '' ) {
 305      if ( empty( $user_nicename ) ) {
 306          return false;
 307      }
 308  
 309      $user = get_user_by( 'slug', $user_nicename );
 310  
 311      /**
 312       * Filters the user ID based on user_nicename.
 313       *
 314       * @since 1.2.3
 315       *
 316       * @param int|null $value         ID of the user or null.
 317       * @param string   $user_nicename User nicename to check.
 318       */
 319      return apply_filters( 'bp_core_get_userid_from_nicename', ! empty( $user->ID ) ? $user->ID : null, $user_nicename );
 320  }
 321  
 322  /**
 323   * Return the username for a user based on their user id.
 324   *
 325   * This function is sensitive to the BP_ENABLE_USERNAME_COMPATIBILITY_MODE,
 326   * so it will return the user_login or user_nicename as appropriate.
 327   *
 328   * @since 1.0.0
 329   *
 330   * @param int         $user_id       User ID to check.
 331   * @param string|bool $user_nicename Optional. user_nicename of user being checked.
 332   * @param string|bool $user_login    Optional. user_login of user being checked.
 333   * @return string The username of the matched user or an empty string if no user is found.
 334   */
 335  function bp_core_get_username( $user_id = 0, $user_nicename = false, $user_login = false ) {
 336  
 337      if ( ! $user_nicename && ! $user_login ) {
 338          // Pull an audible and maybe use the login over the nicename.
 339          if ( bp_is_username_compatibility_mode() ) {
 340              $username = get_the_author_meta( 'login', $user_id );
 341          } else {
 342              $username = get_the_author_meta( 'nicename', $user_id );
 343          }
 344      } else {
 345          $username = bp_is_username_compatibility_mode() ? $user_login : $user_nicename;
 346      }
 347  
 348      /**
 349       * Filters the username based on originally provided user ID.
 350       *
 351       * @since 1.0.1
 352       *
 353       * @param string $username Username determined by user ID.
 354       */
 355      return apply_filters( 'bp_core_get_username', $username );
 356  }
 357  
 358  /**
 359   * Return the user_nicename for a user based on their user_id.
 360   *
 361   * This should be used for linking to user profiles and anywhere else a
 362   * sanitized and unique slug to a user is needed.
 363   *
 364   * @since 1.5.0
 365   *
 366   * @param int $user_id User ID to check.
 367   * @return string The username of the matched user or an empty string if no user is found.
 368   */
 369  function bp_members_get_user_nicename( $user_id ) {
 370  
 371      /**
 372       * Filters the user_nicename based on originally provided user ID.
 373       *
 374       * @since 1.5.0
 375       *
 376       * @param string $username User nice name determined by user ID.
 377       */
 378      return apply_filters( 'bp_members_get_user_nicename', get_the_author_meta( 'nicename', $user_id ) );
 379  }
 380  
 381  /**
 382   * Return the email address for the user based on user ID.
 383   *
 384   * @since 1.0.0
 385   *
 386   * @param int $user_id User ID to check.
 387   * @return string The email for the matched user. Empty string if no user
 388   *                matches the $user_id.
 389   */
 390  function bp_core_get_user_email( $user_id ) {
 391  
 392      /**
 393       * Filters the user email for user based on user ID.
 394       *
 395       * @since 1.0.1
 396       *
 397       * @param string $email Email determined for the user.
 398       */
 399      return apply_filters( 'bp_core_get_user_email', get_the_author_meta( 'email', $user_id ) );
 400  }
 401  
 402  /**
 403   * Return a HTML formatted link for a user with the user's full name as the link text.
 404   *
 405   * Eg: <a href="http://andy.example.com/">Andy Peatling</a>
 406   *
 407   * Optional parameters will return just the name or just the URL.
 408   *
 409   * @since 1.0.0
 410   *
 411   * @param int  $user_id   User ID to check.
 412   * @param bool $no_anchor Disable URL and HTML and just return full name.
 413   *                        Default: false.
 414   * @param bool $just_link Disable full name and HTML and just return the URL
 415   *                        text. Default false.
 416   * @return string|bool The link text based on passed parameters, or false on
 417   *                     no match.
 418   */
 419  function bp_core_get_userlink( $user_id, $no_anchor = false, $just_link = false ) {
 420      $display_name = bp_core_get_user_displayname( $user_id );
 421  
 422      if ( empty( $display_name ) ) {
 423          return false;
 424      }
 425  
 426      if ( ! empty( $no_anchor ) ) {
 427          return $display_name;
 428      }
 429  
 430      if ( !$url = bp_core_get_user_domain( $user_id ) ) {
 431          return false;
 432      }
 433  
 434      if ( ! empty( $just_link ) ) {
 435          return $url;
 436      }
 437  
 438      /**
 439       * Filters the link text for the passed in user.
 440       *
 441       * @since 1.2.0
 442       *
 443       * @param string $value   Link text based on passed parameters.
 444       * @param int    $user_id ID of the user to check.
 445       */
 446      return apply_filters( 'bp_core_get_userlink', '<a href="' . esc_url( $url ) . '">' . $display_name . '</a>', $user_id );
 447  }
 448  
 449  /**
 450   * Fetch the display name for a group of users.
 451   *
 452   * Uses the 'Name' field in xprofile if available. Falls back on WP
 453   * display_name, and then user_nicename.
 454   *
 455   * @since 2.0.0
 456   *
 457   * @param array $user_ids Array of user IDs to get display names for.
 458   * @return array Associative array of the format "id" => "displayname".
 459   */
 460  function bp_core_get_user_displaynames( $user_ids ) {
 461  
 462      // Sanitize.
 463      $user_ids = wp_parse_id_list( $user_ids );
 464  
 465      // Remove dupes and empties.
 466      $user_ids = array_unique( array_filter( $user_ids ) );
 467  
 468      if ( empty( $user_ids ) ) {
 469          return array();
 470      }
 471  
 472      // Warm the WP users cache with a targeted bulk update.
 473      cache_users( $user_ids );
 474  
 475      $retval = array();
 476      foreach ( $user_ids as $user_id ) {
 477          $retval[ $user_id ] = bp_core_get_user_displayname( $user_id );
 478      }
 479  
 480      return $retval;
 481  }
 482  
 483  /**
 484   * Fetch the display name for a user.
 485   *
 486   * @since 1.0.1
 487   *
 488   * @param int|string|bool $user_id_or_username User ID or username.
 489   * @return string|bool The display name for the user in question, or false if
 490   *                     user not found.
 491   */
 492  function bp_core_get_user_displayname( $user_id_or_username ) {
 493      if ( empty( $user_id_or_username ) ) {
 494          return false;
 495      }
 496  
 497      if ( ! is_numeric( $user_id_or_username ) ) {
 498          $user_id = bp_core_get_userid( $user_id_or_username );
 499      } else {
 500          $user_id = $user_id_or_username;
 501      }
 502  
 503      if ( empty( $user_id ) ) {
 504          return false;
 505      }
 506  
 507      /**
 508       * Filters the display name for the passed in user.
 509       *
 510       * @since 1.0.1
 511       *
 512       * @param string $fullname Display name for the user.
 513       * @param int    $user_id  ID of the user to check.
 514       */
 515      return apply_filters( 'bp_core_get_user_displayname', get_the_author_meta( 'display_name', $user_id ), $user_id );
 516  }
 517  add_filter( 'bp_core_get_user_displayname', 'strip_tags', 1 );
 518  add_filter( 'bp_core_get_user_displayname', 'trim' );
 519  add_filter( 'bp_core_get_user_displayname', 'stripslashes' );
 520  add_filter( 'bp_core_get_user_displayname', 'esc_html' );
 521  
 522  /**
 523   * Return the user link for the user based on user email address.
 524   *
 525   * @since 1.0.0
 526   *
 527   * @param string $email The email address for the user.
 528   * @return string The link to the users home base. False on no match.
 529   */
 530  function bp_core_get_userlink_by_email( $email ) {
 531      $user = get_user_by( 'email', $email );
 532  
 533      /**
 534       * Filters the user link for the user based on user email address.
 535       *
 536       * @since 1.0.1
 537       *
 538       * @param string|bool $value URL for the user if found, otherwise false.
 539       */
 540      return apply_filters( 'bp_core_get_userlink_by_email', bp_core_get_userlink( $user->ID, false, false, true ) );
 541  }
 542  
 543  /**
 544   * Return the user link for the user based on the supplied identifier.
 545   *
 546   * @since 1.0.0
 547   *
 548   * @param string $username If BP_ENABLE_USERNAME_COMPATIBILITY_MODE is set,
 549   *                         this should be user_login, otherwise it should
 550   *                         be user_nicename.
 551   * @return string|bool The link to the user's domain, false on no match.
 552   */
 553  function bp_core_get_userlink_by_username( $username ) {
 554      if ( bp_is_username_compatibility_mode() ) {
 555          $user_id = bp_core_get_userid( $username );
 556      } else {
 557          $user_id = bp_core_get_userid_from_nicename( $username );
 558      }
 559  
 560      /**
 561       * Filters the user link for the user based on username.
 562       *
 563       * @since 1.0.1
 564       *
 565       * @param string|bool $value URL for the user if found, otherwise false.
 566       */
 567      return apply_filters( 'bp_core_get_userlink_by_username', bp_core_get_userlink( $user_id, false, false, true ) );
 568  }
 569  
 570  /**
 571   * Return the total number of members for the installation.
 572   *
 573   * Note that this is a raw count of non-spam, activated users. It does not
 574   * account for users who have logged activity (last_active). See
 575   * {@link bp_core_get_active_member_count()}.
 576   *
 577   * @since 1.2.0
 578   *
 579   * @global wpdb $wpdb WordPress database object.
 580   *
 581   * @return int The total number of members.
 582   */
 583  function bp_core_get_total_member_count() {
 584      global $wpdb;
 585  
 586      $count = wp_cache_get( 'bp_total_member_count', 'bp' );
 587  
 588      if ( false === $count ) {
 589          $status_sql = bp_core_get_status_sql();
 590          $count = $wpdb->get_var( "SELECT COUNT(ID) FROM {$wpdb->users} WHERE {$status_sql}" );
 591          wp_cache_set( 'bp_total_member_count', $count, 'bp' );
 592      }
 593  
 594      /**
 595       * Filters the total number of members for the installation.
 596       *
 597       * @since 1.2.0
 598       *
 599       * @param int $count Total number of members.
 600       */
 601      return apply_filters( 'bp_core_get_total_member_count', $count );
 602  }
 603  
 604  /**
 605   * Return the total number of members, limited to those members with last_activity.
 606   *
 607   * @since 1.6.0
 608   *
 609   * @global wpdb $wpdb WordPress database object.
 610   *
 611   * @return int The number of active members.
 612   */
 613  function bp_core_get_active_member_count() {
 614      global $wpdb;
 615  
 616      $count = get_transient( 'bp_active_member_count' );
 617      if ( false === $count ) {
 618          $bp = buddypress();
 619  
 620          // Avoid a costly join by splitting the lookup.
 621          if ( is_multisite() ) {
 622              $sql = "SELECT ID FROM {$wpdb->users} WHERE (user_status != 0 OR deleted != 0 OR user_status != 0)";
 623          } else {
 624              $sql = "SELECT ID FROM {$wpdb->users} WHERE user_status != 0";
 625          }
 626  
 627          $exclude_users     = $wpdb->get_col( $sql );
 628          $exclude_users_sql = !empty( $exclude_users ) ? "AND user_id NOT IN (" . implode( ',', wp_parse_id_list( $exclude_users ) ) . ")" : '';
 629          $count             = (int) $wpdb->get_var( $wpdb->prepare( "SELECT COUNT(user_id) FROM {$bp->members->table_name_last_activity} WHERE component = %s AND type = 'last_activity' {$exclude_users_sql}", $bp->members->id ) );
 630  
 631          set_transient( 'bp_active_member_count', $count );
 632      }
 633  
 634      /**
 635       * Filters the total number of members for the installation limited to those with last_activity.
 636       *
 637       * @since 1.6.0
 638       *
 639       * @param int $count Total number of active members.
 640       */
 641      return apply_filters( 'bp_core_get_active_member_count', $count );
 642  }
 643  
 644  /**
 645   * Update the spam status of the member on multisite configs.
 646   *
 647   * @since 5.0.0
 648   *
 649   * @param int $user_id The user ID to spam or ham.
 650   * @param int $value   0 to mark the user as `ham`, 1 to mark as `spam`.
 651   * @return bool          True if the spam status of the member changed.
 652   *                       False otherwise.
 653   */
 654  function bp_core_update_member_status( $user_id = 0, $value = 0 ) {
 655      if ( ! is_multisite() || ! $user_id ) {
 656          return false;
 657      }
 658  
 659      /**
 660       * The `update_user_status()` function is deprecated since WordPress 5.3.0.
 661       * Continue to use it if WordPress current major version is lower than 5.3.
 662       */
 663      if ( bp_get_major_wp_version() < 5.3 ) {
 664          return update_user_status( $user_id, 'spam', $value );
 665      }
 666  
 667      // Otherwise use the replacement function.
 668      $user = wp_update_user( array(
 669          'ID'   => $user_id,
 670          'spam' => $value,
 671      ) );
 672  
 673      if ( is_wp_error( $user ) ) {
 674          return false;
 675      }
 676  
 677      return true;
 678  }
 679  
 680  /**
 681   * Process a spammed or unspammed user.
 682   *
 683   * This function is called from three places:
 684   *
 685   * - in bp_settings_action_capabilities() (from the front-end)
 686   * - by bp_core_mark_user_spam_admin()    (from wp-admin)
 687   * - bp_core_mark_user_ham_admin()        (from wp-admin)
 688   *
 689   * @since 1.6.0
 690   *
 691   * @global wpdb $wpdb WordPress database object.
 692   *
 693   * @param int    $user_id       The ID of the user being spammed/hammed.
 694   * @param string $status        'spam' if being marked as spam, 'ham' otherwise.
 695   * @param bool   $do_wp_cleanup Optional. True to force the cleanup of WordPress content
 696   *                              and status, otherwise false. Generally, this should
 697   *                              only be false if WordPress is expected to have
 698   *                              performed this cleanup independently, as when hooked
 699   *                              to 'make_spam_user'.
 700   * @return bool True on success, false on failure.
 701   */
 702  function bp_core_process_spammer_status( $user_id, $status, $do_wp_cleanup = true ) {
 703      global $wpdb;
 704  
 705      // Bail if no user ID.
 706      if ( empty( $user_id ) ) {
 707          return;
 708      }
 709  
 710      // Bail if user ID is super admin.
 711      if ( is_super_admin( $user_id ) ) {
 712          return;
 713      }
 714  
 715      // Get the functions file.
 716      if ( is_multisite() ) {
 717          require_once( ABSPATH . 'wp-admin/includes/ms.php' );
 718      }
 719  
 720      $is_spam = ( 'spam' == $status );
 721  
 722      // Only you can prevent infinite loops.
 723      remove_action( 'make_spam_user', 'bp_core_mark_user_spam_admin' );
 724      remove_action( 'make_ham_user',  'bp_core_mark_user_ham_admin' );
 725  
 726      // Force the cleanup of WordPress content and status for multisite configs.
 727      if ( $do_wp_cleanup ) {
 728  
 729          // Mark blogs as spam if the user is the sole admin of a site.
 730          if ( is_multisite() ) {
 731              /*
 732               * No native function to fetch a user's blogs by role, so do it manually.
 733               *
 734               * This logic is mostly copied from get_blogs_of_user().
 735               */
 736              $meta = get_user_meta( $user_id );
 737  
 738              foreach ( $meta as $key => $val ) {
 739                  if ( 'capabilities' !== substr( $key, -12 ) ) {
 740                      continue;
 741                  }
 742                  if ( $wpdb->base_prefix && 0 !== strpos( $key, $wpdb->base_prefix ) ) {
 743                      continue;
 744                  }
 745                  $site_id = str_replace( array( $wpdb->base_prefix, '_capabilities' ), '', $key );
 746                  if ( ! is_numeric( $site_id ) ) {
 747                      continue;
 748                  }
 749  
 750                  $site_id = (int) $site_id;
 751  
 752                  // Do not mark the main or current root blog as spam.
 753                  if ( 1 === $site_id || bp_get_root_blog_id() === $site_id ) {
 754                      continue;
 755                  }
 756  
 757                  // Now, do check for administrator role.
 758                  $role = maybe_unserialize( $val );
 759                  if ( empty( $role['administrator'] ) ) {
 760                      continue;
 761                  }
 762  
 763                  // Check if the site has more than 1 admin. If so, bail.
 764                  $counts = count_users( 'time', $site_id );
 765                  if ( empty( $counts['avail_roles']['administrator'] ) || $counts['avail_roles']['administrator'] > 1 ) {
 766                      continue;
 767                  }
 768  
 769                  // Now we can spam the blog.
 770                  update_blog_status( $site_id, 'spam', $is_spam );
 771              }
 772          }
 773  
 774          // Finally, mark this user as a spammer.
 775          bp_core_update_member_status( $user_id, $is_spam );
 776      }
 777  
 778      // Update the user status.
 779      $wpdb->update( $wpdb->users, array( 'user_status' => $is_spam ), array( 'ID' => $user_id ) );
 780  
 781      // Clean user cache.
 782      clean_user_cache( $user_id );
 783  
 784      if ( ! is_multisite() ) {
 785          // Call multisite actions in single site mode for good measure.
 786          if ( true === $is_spam ) {
 787  
 788              /**
 789               * Fires at end of processing spammer in Dashboard if not multisite and user is spam.
 790               *
 791               * @since 1.5.0
 792               *
 793               * @param int $value user ID.
 794               */
 795              do_action( 'make_spam_user', $user_id );
 796          } else {
 797  
 798              /**
 799               * Fires at end of processing spammer in Dashboard if not multisite and user is not spam.
 800               *
 801               * @since 1.5.0
 802               *
 803               * @param int $value user ID.
 804               */
 805              do_action( 'make_ham_user', $user_id );
 806          }
 807      }
 808  
 809      // Hide this user's activity.
 810      if ( ( true === $is_spam ) && bp_is_active( 'activity' ) ) {
 811          bp_activity_hide_user_activity( $user_id );
 812      }
 813  
 814      // We need a special hook for is_spam so that components can delete data at spam time.
 815      if ( true === $is_spam ) {
 816  
 817          /**
 818           * Fires at the end of the process spammer process if the user is spam.
 819           *
 820           * @since 1.5.0
 821           *
 822           * @param int $value Displayed user ID.
 823           */
 824          do_action( 'bp_make_spam_user', $user_id );
 825      } else {
 826  
 827          /**
 828           * Fires at the end of the process spammer process if the user is not spam.
 829           *
 830           * @since 1.5.0
 831           *
 832           * @param int $value Displayed user ID.
 833           */
 834          do_action( 'bp_make_ham_user', $user_id );
 835      }
 836  
 837      /**
 838       * Fires at the end of the process for hanlding spammer status.
 839       *
 840       * @since 1.5.5
 841       *
 842       * @param int  $user_id ID of the processed user.
 843       * @param bool $is_spam The determined spam status of processed user.
 844       */
 845      do_action( 'bp_core_process_spammer_status', $user_id, $is_spam );
 846  
 847      // Put things back how we found them.
 848      add_action( 'make_spam_user', 'bp_core_mark_user_spam_admin' );
 849      add_action( 'make_ham_user', 'bp_core_mark_user_ham_admin' );
 850  
 851      return true;
 852  }
 853  /**
 854   * Hook to WP's make_spam_user and run our custom BP spam functions.
 855   *
 856   * @since 1.6.0
 857   *
 858   * @param int $user_id The user ID passed from the make_spam_user hook.
 859   */
 860  function bp_core_mark_user_spam_admin( $user_id ) {
 861      bp_core_process_spammer_status( $user_id, 'spam', false );
 862  }
 863  add_action( 'make_spam_user', 'bp_core_mark_user_spam_admin' );
 864  
 865  /**
 866   * Hook to WP's make_ham_user and run our custom BP spam functions.
 867   *
 868   * @since 1.6.0
 869   *
 870   * @param int $user_id The user ID passed from the make_ham_user hook.
 871   */
 872  function bp_core_mark_user_ham_admin( $user_id ) {
 873      bp_core_process_spammer_status( $user_id, 'ham', false );
 874  }
 875  add_action( 'make_ham_user', 'bp_core_mark_user_ham_admin' );
 876  
 877  /**
 878   * Check whether a user has been marked as a spammer.
 879   *
 880   * @since 1.6.0
 881   *
 882   * @global BuddyPress $bp The one true BuddyPress instance.
 883   *
 884   * @param int $user_id The ID for the user.
 885   * @return bool True if spammer, otherwise false.
 886   */
 887  function bp_is_user_spammer( $user_id = 0 ) {
 888  
 889      // No user to check.
 890      if ( empty( $user_id ) ) {
 891          return false;
 892      }
 893  
 894      $bp = buddypress();
 895  
 896      // Assume user is not spam.
 897      $is_spammer = false;
 898  
 899      // Setup our user.
 900      $user = false;
 901  
 902      // Get locally-cached data if available.
 903      switch ( $user_id ) {
 904          case bp_loggedin_user_id() :
 905              $user = ! empty( $bp->loggedin_user->userdata ) ? $bp->loggedin_user->userdata : false;
 906              break;
 907  
 908          case bp_displayed_user_id() :
 909              $user = ! empty( $bp->displayed_user->userdata ) ? $bp->displayed_user->userdata : false;
 910              break;
 911  
 912          case bp_get_member_user_id() :
 913              global $members_template;
 914              $user = isset( $members_template ) && isset( $members_template->member ) ? $members_template->member :  false;
 915              break;
 916      }
 917  
 918      // Manually get userdata if still empty.
 919      if ( empty( $user ) ) {
 920          $user = get_userdata( $user_id );
 921      }
 922  
 923      // No user found.
 924      if ( empty( $user ) ) {
 925          $is_spammer = false;
 926  
 927      // User found.
 928      } else {
 929  
 930          // Check if spam.
 931          if ( !empty( $user->spam ) ) {
 932              $is_spammer = true;
 933          }
 934  
 935          if ( 1 == $user->user_status ) {
 936              $is_spammer = true;
 937          }
 938      }
 939  
 940      /**
 941       * Filters whether a user is marked as a spammer.
 942       *
 943       * @since 1.6.0
 944       *
 945       * @param bool $is_spammer Whether or not user is marked as spammer.
 946       */
 947      return apply_filters( 'bp_is_user_spammer', (bool) $is_spammer );
 948  }
 949  
 950  /**
 951   * Check whether a user has been marked as deleted.
 952   *
 953   * @since 1.6.0
 954   *
 955   * @global BuddyPress $bp The one true BuddyPress instance.
 956   *
 957   * @param int $user_id The ID for the user.
 958   * @return bool True if deleted, otherwise false.
 959   */
 960  function bp_is_user_deleted( $user_id = 0 ) {
 961  
 962      // No user to check.
 963      if ( empty( $user_id ) ) {
 964          return false;
 965      }
 966  
 967      $bp = buddypress();
 968  
 969      // Assume user is not deleted.
 970      $is_deleted = false;
 971  
 972      // Setup our user.
 973      $user = false;
 974  
 975      // Get locally-cached data if available.
 976      switch ( $user_id ) {
 977          case bp_loggedin_user_id() :
 978              $user = ! empty( $bp->loggedin_user->userdata ) ? $bp->loggedin_user->userdata : false;
 979              break;
 980  
 981          case bp_displayed_user_id() :
 982              $user = ! empty( $bp->displayed_user->userdata ) ? $bp->displayed_user->userdata : false;
 983              break;
 984      }
 985  
 986      // Manually get userdata if still empty.
 987      if ( empty( $user ) ) {
 988          $user = get_userdata( $user_id );
 989      }
 990  
 991      // No user found.
 992      if ( empty( $user ) ) {
 993          $is_deleted = true;
 994  
 995      // User found.
 996      } else {
 997  
 998          // Check if deleted.
 999          if ( !empty( $user->deleted ) ) {
1000              $is_deleted = true;
1001          }
1002  
1003          if ( 2 == $user->user_status ) {
1004              $is_deleted = true;
1005          }
1006      }
1007  
1008      /**
1009       * Filters whether a user is marked as deleted.
1010       *
1011       * @since 1.6.0
1012       *
1013       * @param bool $is_deleted Whether or not user is marked as deleted.
1014       */
1015      return apply_filters( 'bp_is_user_deleted', (bool) $is_deleted );
1016  }
1017  
1018  /**
1019   * Check whether a user is "active", ie neither deleted nor spammer.
1020   *
1021   * @since 1.6.0
1022   *
1023   * @param int $user_id Optional. The user ID to check.
1024   * @return bool True if active, otherwise false.
1025   */
1026  function bp_is_user_active( $user_id = 0 ) {
1027  
1028      // Default to current user.
1029      if ( empty( $user_id ) && is_user_logged_in() ) {
1030          $user_id = bp_loggedin_user_id();
1031      }
1032  
1033      // No user to check.
1034      if ( empty( $user_id ) ) {
1035          return false;
1036      }
1037  
1038      // Check spam.
1039      if ( bp_is_user_spammer( $user_id ) ) {
1040          return false;
1041      }
1042  
1043      // Check deleted.
1044      if ( bp_is_user_deleted( $user_id ) ) {
1045          return false;
1046      }
1047  
1048      // Assume true if not spam or deleted.
1049      return true;
1050  }
1051  
1052  /**
1053   * Check whether user is not active.
1054   *
1055   * @since 1.6.0
1056   *
1057   * @param int $user_id Optional. The user ID to check.
1058   * @return bool True if inactive, otherwise false.
1059   */
1060  function bp_is_user_inactive( $user_id = 0 ) {
1061      // Return the inverse of active.
1062      return ! bp_is_user_active( $user_id );
1063  }
1064  
1065  /**
1066   * Update a user's last activity.
1067   *
1068   * @since 1.9.0
1069   * @since 7.0.0 Backward compatibility usermeta mirroring is only allowed if the
1070   *              legacy user query is enabled.
1071   *
1072   * @param int    $user_id Optional. ID of the user being updated.
1073   * @param string $time    Optional. Time of last activity, in 'Y-m-d H:i:s' format.
1074   * @return bool True on success, false on failure.
1075   */
1076  function bp_update_user_last_activity( $user_id = 0, $time = '' ) {
1077  
1078      // Fall back on current user.
1079      if ( empty( $user_id ) ) {
1080          $user_id = bp_loggedin_user_id();
1081      }
1082  
1083      // Bail if the user id is 0, as there's nothing to update.
1084      if ( empty( $user_id ) ) {
1085          return false;
1086      }
1087  
1088      // Fall back on current time.
1089      if ( empty( $time ) ) {
1090          $time = bp_core_current_time();
1091      }
1092  
1093      /** This filter is documented in bp_core_get_users() */
1094      $use_legacy_query = apply_filters( 'bp_use_legacy_user_query', false, __FUNCTION__, [ 'user_id' => $user_id ] );
1095  
1096      /*
1097       * As of BuddyPress 2.0, last_activity is no longer stored in usermeta.
1098       * However, we mirror it there for backward compatibility. Do not use!
1099       *
1100       * As of BuddyPress 7.0, mirroring is only allowed if the legacy user
1101       * query is enabled.
1102       */
1103      if ( $use_legacy_query ) {
1104          remove_filter( 'update_user_metadata', '_bp_update_user_meta_last_activity_warning', 10 );
1105          remove_filter( 'get_user_metadata', '_bp_get_user_meta_last_activity_warning', 10 );
1106          bp_update_user_meta( $user_id, 'last_activity', $time );
1107          add_filter( 'update_user_metadata', '_bp_update_user_meta_last_activity_warning', 10, 4 );
1108          add_filter( 'get_user_metadata', '_bp_get_user_meta_last_activity_warning', 10, 4 );
1109      }
1110  
1111      return BP_Core_User::update_last_activity( $user_id, $time );
1112  }
1113  
1114  /**
1115   * Backward compatibility for 'last_activity' usermeta fetching.
1116   *
1117   * In BuddyPress 2.0, user last_activity data was moved out of usermeta. For
1118   * backward compatibility, we continue to mirror the data there. This function
1119   * serves two purposes: it warns plugin authors of the change, and it returns
1120   * the data from the proper location.
1121   *
1122   * @since 2.0.0
1123   * @since 2.9.3 Added the `$single` parameter.
1124   *
1125   * @access private For internal use only.
1126   *
1127   * @param null   $retval Null retval value.
1128   * @param int    $object_id ID of the user.
1129   * @param string $meta_key  Meta key being fetched.
1130   * @param bool   $single    Whether a single key is being fetched (vs an array).
1131   * @return string|null
1132   */
1133  function _bp_get_user_meta_last_activity_warning( $retval, $object_id, $meta_key, $single ) {
1134      static $warned = false;
1135  
1136      if ( 'last_activity' === $meta_key ) {
1137          // Don't send the warning more than once per pageload.
1138          if ( false === $warned ) {
1139              _doing_it_wrong( 'get_user_meta( $user_id, \'last_activity\' )', __( 'User last_activity data is no longer stored in usermeta. Use bp_get_user_last_activity() instead.', 'buddypress' ), '2.0.0' );
1140              $warned = true;
1141          }
1142  
1143          $user_last_activity = bp_get_user_last_activity( $object_id );
1144          if ( $single ) {
1145              return $user_last_activity;
1146          } else {
1147              return array( $user_last_activity );
1148          }
1149      }
1150  
1151      return $retval;
1152  }
1153  add_filter( 'get_user_metadata', '_bp_get_user_meta_last_activity_warning', 10, 4 );
1154  
1155  /**
1156   * Backward compatibility for 'last_activity' usermeta setting.
1157   *
1158   * In BuddyPress 2.0, user last_activity data was moved out of usermeta. For
1159   * backward compatibility, we continue to mirror the data there. This function
1160   * serves two purposes: it warns plugin authors of the change, and it updates
1161   * the data in the proper location.
1162   *
1163   * @since 2.0.0
1164   *
1165   * @access private For internal use only.
1166   *
1167   * @param int    $meta_id    ID of the just-set usermeta row.
1168   * @param int    $object_id  ID of the user.
1169   * @param string $meta_key   Meta key being fetched.
1170   * @param string $meta_value Active time.
1171   */
1172  function _bp_update_user_meta_last_activity_warning( $meta_id, $object_id, $meta_key, $meta_value ) {
1173      if ( 'last_activity' === $meta_key ) {
1174          _doing_it_wrong( 'update_user_meta( $user_id, \'last_activity\' )', __( 'User last_activity data is no longer stored in usermeta. Use bp_update_user_last_activity() instead.', 'buddypress' ), '2.0.0' );
1175          bp_update_user_last_activity( $object_id, $meta_value );
1176      }
1177  }
1178  add_filter( 'update_user_metadata', '_bp_update_user_meta_last_activity_warning', 10, 4 );
1179  
1180  /**
1181   * Get the last activity for a given user.
1182   *
1183   * @since 1.9.0
1184   *
1185   * @param int $user_id The ID of the user.
1186   * @return string Time of last activity, in 'Y-m-d H:i:s' format, or an empty
1187   *                string if none is found.
1188   */
1189  function bp_get_user_last_activity( $user_id = 0 ) {
1190      $activity = '';
1191  
1192      $last_activity = BP_Core_User::get_last_activity( $user_id );
1193      if ( ! empty( $last_activity[ $user_id ] ) ) {
1194          $activity = $last_activity[ $user_id ]['date_recorded'];
1195      }
1196  
1197      /**
1198       * Filters the last activity for a given user.
1199       *
1200       * @since 1.9.0
1201       *
1202       * @param string $activity Time of last activity, in 'Y-m-d H:i:s' format or
1203       *                         an empty string if none found.
1204       * @param int    $user_id  ID of the user being checked.
1205       */
1206      return apply_filters( 'bp_get_user_last_activity', $activity, $user_id );
1207  }
1208  
1209  /**
1210   * Migrate last_activity data from the usermeta table to the activity table.
1211   *
1212   * Generally, this function is only run when BP is upgraded to 2.0. It can also
1213   * be called directly from the BuddyPress Tools panel.
1214   *
1215   * @since 2.0.0
1216   *
1217   * @global BuddyPress $bp The one true BuddyPress instance.
1218   * @global wpdb $wpdb WordPress database object.
1219   *
1220   * @return bool
1221   */
1222  function bp_last_activity_migrate() {
1223      global $wpdb;
1224  
1225      $bp = buddypress();
1226  
1227      // Wipe out existing last_activity data in the activity table -
1228      // this helps to prevent duplicates when pulling from the usermeta
1229      // table.
1230      $wpdb->query( $wpdb->prepare( "DELETE FROM {$bp->members->table_name_last_activity} WHERE component = %s AND type = 'last_activity'", $bp->members->id ) );
1231  
1232      $sql = "INSERT INTO {$bp->members->table_name_last_activity} (`user_id`, `component`, `type`, `action`, `content`, `primary_link`, `item_id`, `date_recorded` ) (
1233            SELECT user_id, '{$bp->members->id}' as component, 'last_activity' as type, '' as action, '' as content, '' as primary_link, 0 as item_id, meta_value AS date_recorded
1234            FROM {$wpdb->usermeta}
1235            WHERE
1236              meta_key = 'last_activity'
1237      );";
1238  
1239      return $wpdb->query( $sql );
1240  }
1241  
1242  /**
1243   * Fetch every post that is authored by the given user for the current blog.
1244   *
1245   * No longer used in BuddyPress.
1246   *
1247   * @todo Deprecate.
1248   *
1249   * @param int $user_id ID of the user being queried.
1250   * @return array Post IDs.
1251   */
1252  function bp_core_get_all_posts_for_user( $user_id = 0 ) {
1253      global $wpdb;
1254  
1255      if ( empty( $user_id ) ) {
1256          $user_id = bp_displayed_user_id();
1257      }
1258  
1259      return apply_filters( 'bp_core_get_all_posts_for_user', $wpdb->get_col( $wpdb->prepare( "SELECT ID FROM {$wpdb->posts} WHERE post_author = %d AND post_status = 'publish' AND post_type = 'post'", $user_id ) ) );
1260  }
1261  
1262  /**
1263   * Process account deletion requests.
1264   *
1265   * Primarily used for self-deletions, as requested through Settings.
1266   *
1267   * @since 1.0.0
1268   *
1269   * @param int $user_id Optional. ID of the user to be deleted. Default: the
1270   *                     logged-in user.
1271   * @return bool True on success, false on failure.
1272   */
1273  function bp_core_delete_account( $user_id = 0 ) {
1274  
1275      // Use logged in user ID if none is passed.
1276      if ( empty( $user_id ) ) {
1277          $user_id = bp_loggedin_user_id();
1278      }
1279  
1280      // Site admins cannot be deleted.
1281      if ( is_super_admin( $user_id ) ) {
1282          return false;
1283      }
1284  
1285      // Extra checks if user is not deleting themselves.
1286      if ( bp_loggedin_user_id() !== absint( $user_id ) ) {
1287  
1288          // Bail if current user cannot delete any users.
1289          if ( ! bp_current_user_can( 'delete_users' ) ) {
1290              return false;
1291          }
1292  
1293          // Bail if current user cannot delete this user.
1294          if ( ! current_user_can_for_blog( bp_get_root_blog_id(), 'delete_user', $user_id ) ) {
1295              return false;
1296          }
1297      }
1298  
1299      /**
1300       * Fires before the processing of an account deletion.
1301       *
1302       * @since 1.6.0
1303       *
1304       * @param int $user_id ID of the user account being deleted.
1305       */
1306      do_action( 'bp_core_pre_delete_account', $user_id );
1307  
1308      // Specifically handle multi-site environment.
1309      if ( is_multisite() ) {
1310          require_once( ABSPATH . '/wp-admin/includes/ms.php'   );
1311          require_once( ABSPATH . '/wp-admin/includes/user.php' );
1312  
1313          $retval = wpmu_delete_user( $user_id );
1314  
1315      // Single site user deletion.
1316      } else {
1317          require_once( ABSPATH . '/wp-admin/includes/user.php' );
1318          $retval = wp_delete_user( $user_id );
1319      }
1320  
1321      /**
1322       * Fires after the deletion of an account.
1323       *
1324       * @since 1.6.0
1325       *
1326       * @param int $user_id ID of the user account that was deleted.
1327       */
1328      do_action( 'bp_core_deleted_account', $user_id );
1329  
1330      return $retval;
1331  }
1332  
1333  /**
1334   * Determines whether user data should be removed on the 'delete_user' hook.
1335   *
1336   * WordPress's 'delete_user' hook is ambiguous: on a standard installation, it means that a user
1337   * account is being removed from the system, while on Multisite it simply means the user is
1338   * being removed from a specific site (ie its roles are being revoked). As a rule, this means
1339   * that BuddyPress should remove user data on the delete_user hook only on non-Multisite
1340   * installations - only when the user account is being removed altogether. However, this behavior
1341   * can be filtered in a global, per-user, or per-component fashion.
1342   *
1343   * @since 6.0.0
1344   *
1345   * @param string $data_type Type of data to be removed.
1346   * @param int    $user_id   ID of the user, as passed to 'delete_user'.
1347   * @return bool
1348   */
1349  function bp_remove_user_data_on_delete_user_hook( $component, $user_id ) {
1350      $remove = ! is_multisite();
1351  
1352      /**
1353       * Filters whether to remove user data on the 'delete_user' hook.
1354       *
1355       * @param bool   $remove    Whether data should be removed.
1356       * @param string $data_type Type of data to be removed.
1357       * @param int    $user_id   ID of the user, as passed to 'delete_user'.
1358       */
1359      return apply_filters( 'bp_remove_user_data_on_delete_user_hook', $remove, $component, $user_id );
1360  }
1361  
1362  /**
1363   * Delete a user's avatar when the user is deleted.
1364   *
1365   * @since 1.9.0
1366   *
1367   * @param int $user_id ID of the user who is about to be deleted.
1368   * @return bool True on success, false on failure.
1369   */
1370  function bp_core_delete_avatar_on_user_delete( $user_id ) {
1371      return bp_core_delete_existing_avatar( array(
1372          'item_id' => $user_id,
1373          'object'  => 'user',
1374      ) );
1375  }
1376  add_action( 'wpmu_delete_user', 'bp_core_delete_avatar_on_user_delete' );
1377  
1378  /**
1379   * Deletes last_activity data on the 'delete_user' hook.
1380   *
1381   * @since 6.0.0
1382   *
1383   * @param int $user_id The ID of the deleted user.
1384   */
1385  function bp_core_delete_avatar_on_delete_user( $user_id ) {
1386      if ( ! bp_remove_user_data_on_delete_user_hook( 'avatar', $user_id ) ) {
1387          return;
1388      }
1389  
1390      bp_core_delete_avatar_on_user_delete( $user_id );
1391  }
1392  add_action( 'delete_user', 'bp_core_delete_avatar_on_delete_user' );
1393  
1394  /**
1395   * Multibyte-safe ucfirst() support.
1396   *
1397   * Uses multibyte functions when available on the PHP build.
1398   *
1399   * @since 1.0.0
1400   *
1401   * @param string $str String to be upper-cased.
1402   * @return string
1403   */
1404  function bp_core_ucfirst( $str ) {
1405      if ( function_exists( 'mb_strtoupper' ) && function_exists( 'mb_substr' ) ) {
1406          $fc = mb_strtoupper( mb_substr( $str, 0, 1 ) );
1407          return $fc.mb_substr( $str, 1 );
1408      } else {
1409          return ucfirst( $str );
1410      }
1411  }
1412  
1413  /**
1414   * Prevent spammers from logging in.
1415   *
1416   * When a user logs in, check if they have been marked as a spammer. If yes
1417   * then simply redirect them to the home page and stop them from logging in.
1418   *
1419   * @since 1.1.2
1420   *
1421   * @param WP_User|WP_Error $user Either the WP_User object or the WP_Error
1422   *                               object, as passed to the 'authenticate' filter.
1423   * @return WP_User|WP_Error If the user is not a spammer, return the WP_User
1424   *                          object. Otherwise a new WP_Error object.
1425   */
1426  function bp_core_boot_spammer( $user ) {
1427  
1428      // Check to see if the $user has already failed logging in, if so return $user as-is.
1429      if ( is_wp_error( $user ) || empty( $user ) ) {
1430          return $user;
1431      }
1432  
1433      // The user exists; now do a check to see if the user is a spammer
1434      // if the user is a spammer, stop them in their tracks!
1435      if ( is_a( $user, 'WP_User' ) && ( ( is_multisite() && (int) $user->spam ) || 1 == $user->user_status ) ) {
1436          return new WP_Error( 'invalid_username', __( '<strong>Error</strong>: Your account has been marked as a spammer.', 'buddypress' ) );
1437      }
1438  
1439      // User is good to go!
1440      return $user;
1441  }
1442  add_filter( 'authenticate', 'bp_core_boot_spammer', 30 );
1443  
1444  /**
1445   * Delete last_activity data for the user when the user is deleted.
1446   *
1447   * @since 1.0.0
1448   *
1449   * @param int $user_id The user ID for the user to delete usermeta for.
1450   */
1451  function bp_core_remove_data( $user_id ) {
1452  
1453      // Remove last_activity data.
1454      BP_Core_User::delete_last_activity( $user_id );
1455  
1456      // Flush the cache to remove the user from all cached objects.
1457      wp_cache_flush();
1458  }
1459  add_action( 'wpmu_delete_user',  'bp_core_remove_data' );
1460  add_action( 'bp_make_spam_user', 'bp_core_remove_data' );
1461  
1462  /**
1463   * Deletes last_activity data on the 'delete_user' hook.
1464   *
1465   * @since 6.0.0
1466   *
1467   * @param int $user_id The ID of the deleted user.
1468   */
1469  function bp_core_remove_data_on_delete_user( $user_id ) {
1470      if ( ! bp_remove_user_data_on_delete_user_hook( 'last_activity', $user_id ) ) {
1471          return;
1472      }
1473  
1474      bp_core_remove_data( $user_id );
1475  }
1476  add_action( 'delete_user', 'bp_core_remove_data_on_delete_user' );
1477  
1478  /**
1479   * Check whether the logged-in user can edit settings for the displayed user.
1480   *
1481   * @since 1.5.0
1482   *
1483   * @return bool True if editing is allowed, otherwise false.
1484   */
1485  function bp_core_can_edit_settings() {
1486      $status = false;
1487  
1488      if ( bp_is_my_profile() ) {
1489          $status = true;
1490      } elseif ( is_super_admin( bp_displayed_user_id() ) && ! is_super_admin() ) {
1491          $status = false;
1492      } elseif ( bp_current_user_can( 'bp_moderate' ) || current_user_can( 'edit_users' ) ) {
1493          $status = true;
1494      }
1495  
1496      /**
1497       * Filters the status of whether the logged-in user can edit settings for the displayed user or not.
1498       *
1499       * @since 2.8.0
1500       *
1501       * @param bool True if editing is allowed, otherwise false.
1502       */
1503      return apply_filters( 'bp_core_can_edit_settings', $status );
1504  }
1505  
1506  /** Sign-up *******************************************************************/
1507  
1508  /**
1509   * Flush illegal names by getting and setting 'illegal_names' site option.
1510   *
1511   * @since 1.2.5
1512   */
1513  function bp_core_flush_illegal_names() {
1514      $illegal_names = get_site_option( 'illegal_names' );
1515      update_site_option( 'illegal_names', $illegal_names );
1516  }
1517  
1518  /**
1519   * Add BuddyPress-specific items to the illegal_names array.
1520   *
1521   * @since 1.2.7
1522   *
1523   * @param array|string $value Illegal names as being saved defined in
1524   *                            Multisite settings.
1525   * @return array Merged and unique array of illegal names.
1526   */
1527  function bp_core_get_illegal_names( $value = '' ) {
1528  
1529      // Make sure $value is array.
1530      if ( empty( $value ) ) {
1531          $db_illegal_names = array();
1532      }
1533  
1534      if ( is_array( $value ) ) {
1535          $db_illegal_names = $value;
1536      } elseif ( is_string( $value ) ) {
1537          $db_illegal_names = explode( ' ', $value );
1538      }
1539  
1540      // Add the core components' slugs to the banned list even if their components aren't active.
1541      $bp_component_slugs = array(
1542          'groups',
1543          'members',
1544          'forums',
1545          'blogs',
1546          'activity',
1547          'profile',
1548          'friends',
1549          'search',
1550          'settings',
1551          'notifications',
1552          'register',
1553          'activate',
1554      );
1555  
1556      // Core constants.
1557      $slug_constants = array(
1558          'BP_GROUPS_SLUG',
1559          'BP_MEMBERS_SLUG',
1560          'BP_FORUMS_SLUG',
1561          'BP_BLOGS_SLUG',
1562          'BP_ACTIVITY_SLUG',
1563          'BP_XPROFILE_SLUG',
1564          'BP_FRIENDS_SLUG',
1565          'BP_SEARCH_SLUG',
1566          'BP_SETTINGS_SLUG',
1567          'BP_NOTIFICATIONS_SLUG',
1568          'BP_REGISTER_SLUG',
1569          'BP_ACTIVATION_SLUG',
1570      );
1571      foreach ( $slug_constants as $constant ) {
1572          if ( defined( $constant ) ) {
1573              $bp_component_slugs[] = constant( $constant );
1574          }
1575      }
1576  
1577      /**
1578       * Filters the array of default illegal usernames.
1579       *
1580       * @since 1.2.2
1581       *
1582       * @param array $value Merged and unique array of illegal usernames.
1583       */
1584      $filtered_illegal_names = apply_filters( 'bp_core_illegal_usernames', array_merge( array( 'www', 'web', 'root', 'admin', 'main', 'invite', 'administrator' ), $bp_component_slugs ) );
1585  
1586      /**
1587       * Filters the list of illegal usernames from WordPress.
1588       *
1589       * @since 3.0
1590       *
1591       * @param array Array of illegal usernames.
1592       */
1593      $wp_filtered_illegal_names = apply_filters( 'illegal_user_logins', array() );
1594  
1595      // First merge BuddyPress illegal names.
1596      $bp_merged_names = array_merge( (array) $filtered_illegal_names, (array) $db_illegal_names );
1597  
1598      // Then merge WordPress and BuddyPress illegal names.
1599      $merged_names = array_merge( (array) $wp_filtered_illegal_names, (array) $bp_merged_names );
1600  
1601      // Remove duplicates.
1602      $illegal_names = array_unique( (array) $merged_names );
1603  
1604      /**
1605       * Filters the array of default illegal names.
1606       *
1607       * @since 1.2.5
1608       *
1609       * @param array $value Merged and unique array of illegal names.
1610       */
1611      return apply_filters( 'bp_core_illegal_names', $illegal_names );
1612  }
1613  add_filter( 'pre_update_site_option_illegal_names', 'bp_core_get_illegal_names' );
1614  
1615  /**
1616   * Check that an email address is valid for use.
1617   *
1618   * Performs the following checks:
1619   *   - Is the email address well-formed?
1620   *   - Is the email address already used?
1621   *   - If there are disallowed email domains, is the current domain among them?
1622   *   - If there's an email domain whitelest, is the current domain on it?
1623   *
1624   * @since 1.6.2
1625   *
1626   * @param string $user_email The email being checked.
1627   * @return bool|array True if the address passes all checks; otherwise an array
1628   *                    of error codes.
1629   */
1630  function bp_core_validate_email_address( $user_email ) {
1631      $errors = array();
1632  
1633      $user_email = sanitize_email( $user_email );
1634  
1635      // Is the email well-formed?
1636      if ( ! is_email( $user_email ) ) {
1637          $errors['invalid'] = 1;
1638      }
1639  
1640      // Is the email on the Banned Email Domains list?
1641      // Note: This check only works on Multisite.
1642      if ( function_exists( 'is_email_address_unsafe' ) && is_email_address_unsafe( $user_email ) ) {
1643          $errors['domain_banned'] = 1;
1644      }
1645  
1646      // Is the email on the Limited Email Domains list?
1647      // Note: This check only works on Multisite.
1648      $limited_email_domains = get_site_option( 'limited_email_domains' );
1649      if ( is_array( $limited_email_domains ) && empty( $limited_email_domains ) == false ) {
1650          $emaildomain = substr( $user_email, 1 + strpos( $user_email, '@' ) );
1651          if ( ! in_array( $emaildomain, $limited_email_domains ) ) {
1652              $errors['domain_not_allowed'] = 1;
1653          }
1654      }
1655  
1656      // Is the email alreday in use?
1657      if ( email_exists( $user_email ) ) {
1658          $errors['in_use'] = 1;
1659      }
1660  
1661      $retval = ! empty( $errors ) ? $errors : true;
1662  
1663      return $retval;
1664  }
1665  
1666  /**
1667   * Add the appropriate errors to a WP_Error object, given results of a validation test.
1668   *
1669   * Functions like bp_core_validate_email_address() return a structured array
1670   * of error codes. bp_core_add_validation_error_messages() takes this array and
1671   * parses, adding the appropriate error messages to the WP_Error object.
1672   *
1673   * @since 1.7.0
1674   *
1675   * @see bp_core_validate_email_address()
1676   *
1677   * @param WP_Error $errors             WP_Error object.
1678   * @param array    $validation_results The return value of a validation function
1679   *                                     like bp_core_validate_email_address().
1680   */
1681  function bp_core_add_validation_error_messages( WP_Error $errors, $validation_results ) {
1682      if ( ! empty( $validation_results['invalid'] ) ) {
1683          $errors->add( 'user_email', __( 'Please check your email address.', 'buddypress' ) );
1684      }
1685  
1686      if ( ! empty( $validation_results['domain_banned'] ) ) {
1687          $errors->add( 'user_email',  __( 'Sorry, that email address is not allowed!', 'buddypress' ) );
1688      }
1689  
1690      if ( ! empty( $validation_results['domain_not_allowed'] ) ) {
1691          $errors->add( 'user_email', __( 'Sorry, that email address is not allowed!', 'buddypress' ) );
1692      }
1693  
1694      if ( ! empty( $validation_results['in_use'] ) ) {
1695          $errors->add( 'user_email', __( 'Sorry, that email address is already used!', 'buddypress' ) );
1696      }
1697  }
1698  
1699  /**
1700   * Validate a user name and email address when creating a new user.
1701   *
1702   * @since 1.2.2
1703   *
1704   * @param string $user_name  Username to validate.
1705   * @param string $user_email Email address to validate.
1706   * @return array Results of user validation including errors, if any.
1707   */
1708  function bp_core_validate_user_signup( $user_name, $user_email ) {
1709  
1710      // Make sure illegal names include BuddyPress slugs and values.
1711      bp_core_flush_illegal_names();
1712  
1713      // WordPress Multisite has its own validation. Use it, so that we
1714      // properly mirror restrictions on username, etc.
1715      if ( function_exists( 'wpmu_validate_user_signup' ) ) {
1716          $result = wpmu_validate_user_signup( $user_name, $user_email );
1717  
1718      // When not running Multisite, we perform our own validation. What
1719      // follows reproduces much of the logic of wpmu_validate_user_signup(),
1720      // minus the multisite-specific restrictions on user_login.
1721      } else {
1722          $errors = new WP_Error();
1723  
1724          /**
1725           * Filters the username before being validated.
1726           *
1727           * @since 1.5.5
1728           *
1729           * @param string $user_name Username to validate.
1730           */
1731          $user_name = apply_filters( 'pre_user_login', $user_name );
1732  
1733          // User name can't be empty.
1734          if ( empty( $user_name ) ) {
1735              $errors->add( 'user_name', __( 'Please enter a username', 'buddypress' ) );
1736          }
1737  
1738          // User name can't be on the list of illegal names.
1739          $illegal_names = get_site_option( 'illegal_names' );
1740          if ( in_array( $user_name, (array) $illegal_names ) ) {
1741              $errors->add( 'user_name', __( 'That username is not allowed', 'buddypress' ) );
1742          }
1743  
1744          // User name must pass WP's validity check.
1745          if ( ! validate_username( $user_name ) ) {
1746              $errors->add( 'user_name', __( 'Usernames can contain only letters, numbers, ., -, and @', 'buddypress' ) );
1747          }
1748  
1749          // Minimum of 4 characters.
1750          if ( strlen( $user_name ) < 4 ) {
1751              $errors->add( 'user_name',  __( 'Username must be at least 4 characters', 'buddypress' ) );
1752          }
1753  
1754          // No underscores. @todo Why not?
1755          if ( false !== strpos( ' ' . $user_name, '_' ) ) {
1756              $errors->add( 'user_name', __( 'Sorry, usernames may not contain the character "_"!', 'buddypress' ) );
1757          }
1758  
1759          // No usernames that are all numeric. @todo Why?
1760          $match = array();
1761          preg_match( '/[0-9]*/', $user_name, $match );
1762          if ( $match[0] == $user_name ) {
1763              $errors->add( 'user_name', __( 'Sorry, usernames must have letters too!', 'buddypress' ) );
1764          }
1765  
1766          // Check into signups.
1767          $signups = BP_Signup::get( array(
1768              'user_login' => $user_name,
1769          ) );
1770  
1771          $signup = isset( $signups['signups'] ) && ! empty( $signups['signups'][0] ) ? $signups['signups'][0] : false;
1772  
1773          // Check if the username has been used already.
1774          if ( username_exists( $user_name ) || ! empty( $signup ) ) {
1775              $errors->add( 'user_name', __( 'Sorry, that username already exists!', 'buddypress' ) );
1776          }
1777  
1778          // Validate the email address and process the validation results into
1779          // error messages.
1780          $validate_email = bp_core_validate_email_address( $user_email );
1781          bp_core_add_validation_error_messages( $errors, $validate_email );
1782  
1783          // Assemble the return array.
1784          $result = array(
1785              'user_name'  => $user_name,
1786              'user_email' => $user_email,
1787              'errors'     => $errors,
1788          );
1789  
1790          // Apply WPMU legacy filter.
1791          $result = apply_filters( 'wpmu_validate_user_signup', $result );
1792      }
1793  
1794      /**
1795       * Filters the result of the user signup validation.
1796       *
1797       * @since 1.2.2
1798       *
1799       * @param array $result Results of user validation including errors, if any.
1800       */
1801      return apply_filters( 'bp_core_validate_user_signup', $result );
1802  }
1803  
1804  /**
1805   * Validate a user password.
1806   *
1807   * @since 7.0.0
1808   *
1809   * @param string       $pass         The password.
1810   * @param string       $confirm_pass The confirmed password.
1811   * @param null|WP_User $userdata     Null or the userdata object when a member updates their password from front-end.
1812   * @return WP_Error A WP error object possibly containing error messages.
1813   */
1814  function bp_members_validate_user_password( $pass, $confirm_pass, $userdata = null ) {
1815      $errors = new WP_Error();
1816  
1817      if ( ! $pass || ! $confirm_pass ) {
1818          $errors->add( 'missing_user_password', __( 'Please make sure you enter your password twice', 'buddypress' ) );
1819      }
1820  
1821      if ( $pass && $confirm_pass && $pass !== $confirm_pass ) {
1822          $errors->add( 'mismatching_user_password', __( 'The passwords you entered do not match.', 'buddypress' ) );
1823      }
1824  
1825      /**
1826       * Filter here to add password validation errors.
1827       *
1828       * @since 7.0.0
1829       *
1830       * @param WP_Error     $errors       Password validation errors.
1831       * @param string       $pass         The password.
1832       * @param string       $confirm_pass The confirmed password.
1833       * @param null|WP_User $userdata     Null or the userdata object when a member updates their password from front-end.
1834       */
1835      return apply_filters( 'bp_members_validate_user_password', $errors, $pass, $confirm_pass, $userdata );
1836  }
1837  
1838  /**
1839   * Validate blog URL and title provided at signup.
1840   *
1841   * @since 1.2.2
1842   *
1843   * @todo Why do we have this wrapper?
1844   *
1845   * @param string $blog_url   Blog URL requested during registration.
1846   * @param string $blog_title Blog title requested during registration.
1847   * @return array
1848   */
1849  function bp_core_validate_blog_signup( $blog_url, $blog_title ) {
1850      if ( ! is_multisite() || ! function_exists( 'wpmu_validate_blog_signup' ) ) {
1851          return false;
1852      }
1853  
1854      /**
1855       * Filters the validated blog url and title provided at signup.
1856       *
1857       * @since 1.2.2
1858       *
1859       * @param array $value Array with the new site data and error messages.
1860       */
1861      return apply_filters( 'bp_core_validate_blog_signup', wpmu_validate_blog_signup( $blog_url, $blog_title ) );
1862  }
1863  
1864  /**
1865   * Process data submitted at user registration and convert to a signup object.
1866   *
1867   * @since 1.2.0
1868   *
1869   * @global BuddyPress $bp The one true BuddyPress instance.
1870   *
1871   * @todo There appears to be a bug in the return value on success.
1872   *
1873   * @param string $user_login    Login name requested by the user.
1874   * @param string $user_password Password requested by the user.
1875   * @param string $user_email    Email address entered by the user.
1876   * @param array  $usermeta      Miscellaneous metadata about the user (blog-specific
1877   *                              signup data, xprofile data, etc).
1878   * @return int|false True on success, WP_Error on failure.
1879   */
1880  function bp_core_signup_user( $user_login, $user_password, $user_email, $usermeta ) {
1881      $bp = buddypress();
1882  
1883      // We need to cast $user_id to pass to the filters.
1884      $user_id = false;
1885  
1886      // Multisite installs have their own install procedure.
1887      if ( is_multisite() ) {
1888          wpmu_signup_user( $user_login, $user_email, $usermeta );
1889  
1890      } else {
1891          // Format data.
1892          $user_login     = preg_replace( '/\s+/', '', sanitize_user( $user_login, true ) );
1893          $user_email     = sanitize_email( $user_email );
1894          $activation_key = wp_generate_password( 32, false );
1895  
1896          /**
1897           * WordPress's default behavior is to create user accounts
1898           * immediately at registration time. BuddyPress uses a system
1899           * borrowed from WordPress Multisite, where signups are stored
1900           * separately and accounts are only created at the time of
1901           * activation. For backward compatibility with plugins that may
1902           * be anticipating WP's default behavior, BP silently creates
1903           * accounts for registrations (though it does not use them). If
1904           * you know that you are not running any plugins dependent on
1905           * these pending accounts, you may want to save a little DB
1906           * clutter by defining setting the BP_SIGNUPS_SKIP_USER_CREATION
1907           * to true in your wp-config.php file.
1908           */
1909          if ( ! defined( 'BP_SIGNUPS_SKIP_USER_CREATION' ) || ! BP_SIGNUPS_SKIP_USER_CREATION ) {
1910              $user_id = BP_Signup::add_backcompat( $user_login, $user_password, $user_email, $usermeta );
1911  
1912              if ( is_wp_error( $user_id ) ) {
1913                  return $user_id;
1914              }
1915  
1916              bp_update_user_meta( $user_id, 'activation_key', $activation_key );
1917          }
1918  
1919          $args = array(
1920              'user_login'     => $user_login,
1921              'user_email'     => $user_email,
1922              'activation_key' => $activation_key,
1923              'meta'           => $usermeta,
1924          );
1925  
1926          BP_Signup::add( $args );
1927  
1928          /**
1929           * Filters if BuddyPress should send an activation key for a new signup.
1930           *
1931           * @since 1.2.3
1932           *
1933           * @param bool   $value          Whether or not to send the activation key.
1934           * @param int    $user_id        User ID to send activation key to.
1935           * @param string $user_email     User email to send activation key to.
1936           * @param string $activation_key Activation key to be sent.
1937           * @param array  $usermeta       Miscellaneous metadata about the user (blog-specific
1938           *                               signup data, xprofile data, etc).
1939           */
1940          if ( apply_filters( 'bp_core_signup_send_activation_key', true, $user_id, $user_email, $activation_key, $usermeta ) ) {
1941              $salutation = $user_login;
1942              if ( bp_is_active( 'xprofile' ) && isset( $usermeta[ 'field_' . bp_xprofile_fullname_field_id() ] ) ) {
1943                  $salutation = $usermeta[ 'field_' . bp_xprofile_fullname_field_id() ];
1944              }
1945  
1946              bp_core_signup_send_validation_email( $user_id, $user_email, $activation_key, $salutation );
1947          }
1948      }
1949  
1950      $bp->signup->username = $user_login;
1951  
1952      /**
1953       * Fires at the end of the process to sign up a user.
1954       *
1955       * @since 1.2.2
1956       *
1957       * @param bool|WP_Error   $user_id       True on success, WP_Error on failure.
1958       * @param string          $user_login    Login name requested by the user.
1959       * @param string          $user_password Password requested by the user.
1960       * @param string          $user_email    Email address requested by the user.
1961       * @param array           $usermeta      Miscellaneous metadata about the user (blog-specific
1962       *                                       signup data, xprofile data, etc).
1963       */
1964      do_action( 'bp_core_signup_user', $user_id, $user_login, $user_password, $user_email, $usermeta );
1965  
1966      return $user_id;
1967  }
1968  
1969  /**
1970   * Create a blog and user based on data supplied at user registration.
1971   *
1972   * @since 1.2.2
1973   *
1974   * @param string $blog_domain Domain requested by user.
1975   * @param string $blog_path   Path requested by user.
1976   * @param string $blog_title  Title as entered by user.
1977   * @param string $user_name   user_login of requesting user.
1978   * @param string $user_email  Email address of requesting user.
1979   * @param string $usermeta    Miscellaneous metadata for the user.
1980   * @return bool
1981   */
1982  function bp_core_signup_blog( $blog_domain, $blog_path, $blog_title, $user_name, $user_email, $usermeta ) {
1983      if ( ! is_multisite() || ! function_exists( 'wpmu_signup_blog' ) ) {
1984          return false;
1985      }
1986  
1987      /**
1988       * Filters the result of wpmu_signup_blog().
1989       *
1990       * This filter provides no value and is retained for
1991       * backwards compatibility.
1992       *
1993       * @since 1.2.2
1994       *
1995       * @param void $value
1996       */
1997      return apply_filters( 'bp_core_signup_blog', wpmu_signup_blog( $blog_domain, $blog_path, $blog_title, $user_name, $user_email, $usermeta ) );
1998  }
1999  
2000  /**
2001   * Activate a signup, as identified by an activation key.
2002   *
2003   * @since 1.2.2
2004   *
2005   * @global wpdb $wpdb WordPress database object.
2006   *
2007   * @param string $key Activation key.
2008   * @return int|bool User ID on success, false on failure.
2009   */
2010  function bp_core_activate_signup( $key ) {
2011      global $wpdb;
2012  
2013      $user = false;
2014  
2015      // Multisite installs have their own activation routine.
2016      if ( is_multisite() ) {
2017          $user = wpmu_activate_signup( $key );
2018  
2019          // If there were errors, add a message and redirect.
2020          if ( ! empty( $user->errors ) ) {
2021              return $user;
2022          }
2023  
2024          $user_id = $user['user_id'];
2025  
2026      } else {
2027          $signups = BP_Signup::get( array(
2028              'activation_key' => $key,
2029          ) );
2030  
2031          if ( empty( $signups['signups'] ) ) {
2032              return new WP_Error( 'invalid_key', __( 'Invalid activation key.', 'buddypress' ) );
2033          }
2034  
2035          $signup = $signups['signups'][0];
2036  
2037          if ( $signup->active ) {
2038              if ( empty( $signup->domain ) ) {
2039                  return new WP_Error( 'already_active', __( 'The user is already active.', 'buddypress' ), $signup );
2040              } else {
2041                  return new WP_Error( 'already_active', __( 'The site is already active.', 'buddypress' ), $signup );
2042              }
2043          }
2044  
2045          // Password is hashed again in wp_insert_user.
2046          $password = wp_generate_password( 12, false );
2047  
2048          $user_id = username_exists( $signup->user_login );
2049  
2050          // Create the user. This should only be necessary if BP_SIGNUPS_SKIP_USER_CREATION is true.
2051          if ( ! $user_id ) {
2052              $user_id = wp_create_user( $signup->user_login, $password, $signup->user_email );
2053  
2054          // Otherwise, update the existing user's status.
2055          } elseif ( $key === bp_get_user_meta( $user_id, 'activation_key', true ) || $key === wp_hash( $user_id ) ) {
2056  
2057              // Change the user's status so they become active.
2058              if ( ! $wpdb->query( $wpdb->prepare( "UPDATE {$wpdb->users} SET user_status = 0 WHERE ID = %d", $user_id ) ) ) {
2059                  return new WP_Error( 'invalid_key', __( 'Invalid activation key.', 'buddypress' ) );
2060              }
2061  
2062              bp_delete_user_meta( $user_id, 'activation_key' );
2063  
2064              $user_already_created = true;
2065  
2066          } else {
2067              $user_already_exists = true;
2068          }
2069  
2070          if ( ! $user_id ) {
2071              return new WP_Error( 'create_user', __( 'Could not create user', 'buddypress' ), $signup );
2072          }
2073  
2074          // Fetch the signup so we have the data later on.
2075          $signups = BP_Signup::get( array(
2076              'activation_key' => $key,
2077          ) );
2078  
2079          $signup = isset( $signups['signups'] ) && ! empty( $signups['signups'][0] ) ? $signups['signups'][0] : false;
2080  
2081          // Activate the signup.
2082          BP_Signup::validate( $key );
2083  
2084          if ( isset( $user_already_exists ) ) {
2085              return new WP_Error( 'user_already_exists', __( 'That username is already activated.', 'buddypress' ), $signup );
2086          }
2087  
2088          // Set up data to pass to the legacy filter.
2089          $user = array(
2090              'user_id'  => $user_id,
2091              'password' => isset( $signup->meta['password'] ) ? $signup->meta['password'] : '',
2092              'meta'     => $signup->meta,
2093          );
2094  
2095          /**
2096           * Maybe notify the site admin of a new user registration.
2097           *
2098           * @since 1.2.2
2099           *
2100           * @param bool $notification Whether to send the notification or not.
2101           */
2102          if ( apply_filters( 'bp_core_send_user_registration_admin_notification', true ) ) {
2103              wp_new_user_notification( $user_id );
2104          }
2105  
2106          if ( isset( $user_already_created ) ) {
2107  
2108              /**
2109               * Fires if the user has already been created.
2110               *
2111               * @since 1.2.2
2112               *
2113               * @param int    $user_id ID of the user being checked.
2114               * @param string $key     Activation key.
2115               * @param array  $user    Array of user data.
2116               */
2117              do_action( 'bp_core_activated_user', $user_id, $key, $user );
2118              return $user_id;
2119          }
2120      }
2121  
2122      // Set any profile data.
2123      if ( bp_is_active( 'xprofile' ) ) {
2124          if ( ! empty( $user['meta']['profile_field_ids'] ) ) {
2125              $profile_field_ids = explode( ',', $user['meta']['profile_field_ids'] );
2126  
2127              foreach( (array) $profile_field_ids as $field_id ) {
2128                  $current_field = isset( $user['meta']["field_{$field_id}"] ) ? $user['meta']["field_{$field_id}"] : false;
2129  
2130                  if ( ! empty( $current_field ) ) {
2131                      xprofile_set_field_data( $field_id, $user_id, $current_field );
2132                  }
2133  
2134                  /*
2135                   * Save the visibility level.
2136                   *
2137                   * Use the field's default visibility if not present, and 'public' if a
2138                   * default visibility is not defined.
2139                   */
2140                  $key = "field_{$field_id}_visibility";
2141                  if ( isset( $user['meta'][ $key ] ) ) {
2142                      $visibility_level = $user['meta'][ $key ];
2143                  } else {
2144                      $vfield           = xprofile_get_field( $field_id, null, false );
2145                      $visibility_level = isset( $vfield->default_visibility ) ? $vfield->default_visibility : 'public';
2146                  }
2147                  xprofile_set_field_visibility_level( $field_id, $user_id, $visibility_level );
2148              }
2149          }
2150      }
2151  
2152      // Replace the password automatically generated by WordPress by the one the user chose.
2153      if ( ! empty( $user['meta']['password'] ) ) {
2154          $wpdb->query( $wpdb->prepare( "UPDATE {$wpdb->users} SET user_pass = %s WHERE ID = %d", $user['meta']['password'], $user_id ) );
2155  
2156          /**
2157           * Make sure to clean the user's cache as we've
2158           * directly edited the password without using
2159           * wp_update_user().
2160           *
2161           * If we can't use wp_update_user() that's because
2162           * we already hashed the password at the signup step.
2163           */
2164          $uc = wp_cache_get( $user_id, 'users' );
2165  
2166          if ( ! empty( $uc->ID ) ) {
2167              clean_user_cache( $uc->ID );
2168          }
2169      }
2170  
2171      /**
2172       * Fires at the end of the user activation process.
2173       *
2174       * @since 1.2.2
2175       *
2176       * @param int    $user_id ID of the user being checked.
2177       * @param string $key     Activation key.
2178       * @param array  $user    Array of user data.
2179       */
2180      do_action( 'bp_core_activated_user', $user_id, $key, $user );
2181  
2182      return $user_id;
2183  }
2184  
2185  /**
2186   * Add default WordPress role for new signups on the BP root blog.
2187   *
2188   * @since 3.0.0
2189   *
2190   * @param int $user_id The user ID to add the default role for.
2191   */
2192  function bp_members_add_role_after_activation( $user_id ) {
2193      // Get default role to add.
2194      $role = bp_get_option( 'default_role' );
2195  
2196      // Multisite.
2197      if ( is_multisite() && ! is_user_member_of_blog( $user_id, bp_get_root_blog_id() ) ) {
2198          add_user_to_blog( bp_get_root_blog_id(), $user_id, $role );
2199  
2200      // Single-site.
2201      } elseif ( ! is_multisite() ) {
2202          $member = get_userdata( $user_id );
2203          $member->set_role( $role );
2204      }
2205  }
2206  add_action( 'bp_core_activated_user', 'bp_members_add_role_after_activation', 1 );
2207  
2208  /**
2209   * Migrate signups from pre-2.0 configuration to wp_signups.
2210   *
2211   * @since 2.0.1
2212   *
2213   * @global wpdb $wpdb WordPress database object.
2214   */
2215  function bp_members_migrate_signups() {
2216      global $wpdb;
2217  
2218      $status_2_ids = $wpdb->get_col( "SELECT ID FROM {$wpdb->users} WHERE user_status = '2'" );
2219  
2220      if ( ! empty( $status_2_ids ) ) {
2221          $signups = get_users( array(
2222              'fields'  => array(
2223                  'ID',
2224                  'user_login',
2225                  'user_pass',
2226                  'user_registered',
2227                  'user_email',
2228                  'display_name',
2229              ),
2230              'include' => $status_2_ids,
2231          ) );
2232  
2233          // Fetch activation keys separately, to avoid the all_with_meta
2234          // overhead.
2235          $status_2_ids_sql = implode( ',', $status_2_ids );
2236          $ak_data = $wpdb->get_results( "SELECT user_id, meta_value FROM {$wpdb->usermeta} WHERE meta_key = 'activation_key' AND user_id IN ({$status_2_ids_sql})" );
2237  
2238          // Rekey.
2239          $activation_keys = array();
2240          foreach ( $ak_data as $ak_datum ) {
2241              $activation_keys[ intval( $ak_datum->user_id ) ] = $ak_datum->meta_value;
2242          }
2243  
2244          unset( $status_2_ids_sql, $status_2_ids, $ak_data );
2245  
2246          // Merge.
2247          foreach ( $signups as &$signup ) {
2248              if ( isset( $activation_keys[ $signup->ID ] ) ) {
2249                  $signup->activation_key = $activation_keys[ $signup->ID ];
2250              }
2251          }
2252  
2253          // Reset the signup var as we're using it to process the migration.
2254          unset( $signup );
2255  
2256      } else {
2257          return;
2258      }
2259  
2260      foreach ( $signups as $signup ) {
2261          $meta = array();
2262  
2263          // Rebuild the activation key, if missing.
2264          if ( empty( $signup->activation_key ) ) {
2265              $signup->activation_key = wp_generate_password( 32, false );
2266          }
2267  
2268          if ( bp_is_active( 'xprofile' ) ) {
2269              $meta['field_1'] = $signup->display_name;
2270          }
2271  
2272          $meta['password'] = $signup->user_pass;
2273  
2274          $user_login = preg_replace( '/\s+/', '', sanitize_user( $signup->user_login, true ) );
2275          $user_email = sanitize_email( $signup->user_email );
2276  
2277          BP_Signup::add( array(
2278              'user_login'     => $user_login,
2279              'user_email'     => $user_email,
2280              'registered'     => $signup->user_registered,
2281              'activation_key' => $signup->activation_key,
2282              'meta'           => $meta
2283          ) );
2284  
2285          // Deleting these options will remove signups from users count.
2286          delete_user_option( $signup->ID, 'capabilities' );
2287          delete_user_option( $signup->ID, 'user_level'   );
2288      }
2289  }
2290  
2291  /**
2292   * Map a user's WP display name to the XProfile fullname field, if necessary.
2293   *
2294   * This only happens when a user is registered in wp-admin by an administrator;
2295   * during normal registration, XProfile data is provided directly by the user.
2296   *
2297   * @since 1.2.0
2298   *
2299   * @param int $user_id ID of the user.
2300   * @return bool
2301   */
2302  function bp_core_map_user_registration( $user_id ) {
2303  
2304      // Only map data when the site admin is adding users, not on registration.
2305      if ( ! is_admin() ) {
2306          return false;
2307      }
2308  
2309      // Add the user's fullname to Xprofile.
2310      if ( bp_is_active( 'xprofile' ) ) {
2311          $firstname = bp_get_user_meta( $user_id, 'first_name', true );
2312          $lastname = ' ' . bp_get_user_meta( $user_id, 'last_name', true );
2313          $name = $firstname . $lastname;
2314  
2315          if ( empty( $name ) || ' ' == $name ) {
2316              $name = bp_get_user_meta( $user_id, 'nickname', true );
2317          }
2318  
2319          xprofile_set_field_data( 1, $user_id, $name );
2320      }
2321  }
2322  add_action( 'user_register', 'bp_core_map_user_registration' );
2323  
2324  /**
2325   * Get the avatar storage directory for use during registration.
2326   *
2327   * @since 1.1.0
2328   *
2329   * @global BuddyPress $bp The one true BuddyPress instance.
2330   *
2331   * @return string|bool Directory path on success, false on failure.
2332   */
2333  function bp_core_signup_avatar_upload_dir() {
2334      $bp = buddypress();
2335  
2336      if ( empty( $bp->signup->avatar_dir ) ) {
2337          return false;
2338      }
2339  
2340      $directory = 'avatars/signups';
2341      $path      = bp_core_avatar_upload_path() . '/' . $directory . '/' . $bp->signup->avatar_dir;
2342      $newbdir   = $path;
2343      $newurl    = bp_core_avatar_url() . '/' . $directory . '/' . $bp->signup->avatar_dir;
2344      $newburl   = $newurl;
2345      $newsubdir = '/' . $directory . '/' . $bp->signup->avatar_dir;
2346  
2347      /**
2348       * Filters the avatar storage directory for use during registration.
2349       *
2350       * @since 1.1.1
2351       *
2352       * @param array $value Array of path and URL values for created storage directory.
2353       */
2354      return apply_filters( 'bp_core_signup_avatar_upload_dir', array(
2355          'path'    => $path,
2356          'url'     => $newurl,
2357          'subdir'  => $newsubdir,
2358          'basedir' => $newbdir,
2359          'baseurl' => $newburl,
2360          'error'   => false,
2361      ) );
2362  }
2363  
2364  /**
2365   * Send activation email to a newly registered user.
2366   *
2367   * @since 1.2.2
2368   * @since 2.5.0 Add the $user_login parameter.
2369   * @since 5.0.0 Change $user_login parameter to more general $salutation.
2370   *
2371   * @param int|bool $user_id    ID of the new user, false if BP_SIGNUPS_SKIP_USER_CREATION is true.
2372   * @param string   $user_email   Email address of the new user.
2373   * @param string   $key          Activation key.
2374   * @param string   $salutation   Optional. The name to be used as a salutation in the email.
2375   */
2376  function bp_core_signup_send_validation_email( $user_id, $user_email, $key, $salutation = '' ) {
2377      $args = array(
2378          'tokens' => array(
2379              'activate.url' => esc_url( trailingslashit( bp_get_activation_page() ) . "{$key}/" ),
2380              'key'          => $key,
2381              'user.email'   => $user_email,
2382              'user.id'      => $user_id,
2383          ),
2384      );
2385  
2386      $to = array( array( $user_email => $salutation ) );
2387  
2388      bp_send_email( 'core-user-registration', $to, $args );
2389  
2390      // Record that the activation email has been sent.
2391      $signups = BP_Signup::get(
2392          array(
2393              'activation_key' => $key,
2394          )
2395      );
2396  
2397      if ( ! empty( $signups['signups'] ) ) {
2398          foreach ( $signups['signups'] as $signup ) {
2399              $meta = array(
2400                  'sent_date'  => current_time( 'mysql', true ),
2401                  'count_sent' => $signup->count_sent + 1
2402              );
2403  
2404              BP_Signup::update( array(
2405                  'signup_id' => $signup->id,
2406                  'meta'      => $meta,
2407              ) );
2408          }
2409      }
2410  }
2411  
2412  /**
2413   * Display a "resend email" link when an unregistered user attempts to log in.
2414   *
2415   * @since 1.2.2
2416   *
2417   * @param WP_User|WP_Error|null $user     Either the WP_User or the WP_Error object.
2418   * @param string                $username The inputted, attempted username.
2419   * @param string                $password The inputted, attempted password.
2420   * @return WP_User|WP_Error
2421   */
2422  function bp_core_signup_disable_inactive( $user = null, $username = '', $password ='' ) {
2423      // Login form not used.
2424      if ( empty( $username ) && empty( $password ) ) {
2425          return $user;
2426      }
2427  
2428      // An existing WP_User with a user_status of 2 is either a legacy
2429      // signup, or is a user created for backward compatibility. See
2430      // {@link bp_core_signup_user()} for more details.
2431      if ( is_a( $user, 'WP_User' ) && 2 == $user->user_status ) {
2432          $user_login = $user->user_login;
2433  
2434      // If no WP_User is found corresponding to the username, this
2435      // is a potential signup.
2436      } elseif ( is_wp_error( $user ) && 'invalid_username' == $user->get_error_code() ) {
2437          $user_login = $username;
2438  
2439      // This is an activated user, so bail.
2440      } else {
2441          return $user;
2442      }
2443  
2444      // Look for the unactivated signup corresponding to the login name.
2445      $signup = BP_Signup::get( array( 'user_login' => sanitize_user( $user_login ) ) );
2446  
2447      // No signup or more than one, something is wrong. Let's bail.
2448      if ( empty( $signup['signups'][0] ) || $signup['total'] > 1 ) {
2449          return $user;
2450      }
2451  
2452      // Unactivated user account found!
2453      // Set up the feedback message.
2454      $signup_id = $signup['signups'][0]->signup_id;
2455  
2456      $resend_url_params = array(
2457          'action' => 'bp-resend-activation',
2458          'id'     => $signup_id,
2459      );
2460  
2461      $resend_url = wp_nonce_url(
2462          add_query_arg( $resend_url_params, wp_login_url() ),
2463          'bp-resend-activation'
2464      );
2465  
2466      $resend_string = '<br /><br />';
2467  
2468      /* translators: %s: the activation url */
2469      $resend_string .= sprintf( __( 'If you have not received an email yet, <a href="%s">click here to resend it</a>.', 'buddypress' ), esc_url( $resend_url ) );
2470  
2471      return new WP_Error( 'bp_account_not_activated', __( '<strong>Error</strong>: Your account has not been activated. Check your email for the activation link.', 'buddypress' ) . $resend_string );
2472  }
2473  add_filter( 'authenticate', 'bp_core_signup_disable_inactive', 30, 3 );
2474  
2475  /**
2476   * On the login screen, resends the activation email for a user.
2477   *
2478   * @since 2.0.0
2479   *
2480   * @see bp_core_signup_disable_inactive()
2481   */
2482  function bp_members_login_resend_activation_email() {
2483      global $error;
2484  
2485      if ( empty( $_GET['id'] ) || empty( $_GET['_wpnonce'] ) ) {
2486          return;
2487      }
2488  
2489      // Verify nonce.
2490      if ( ! wp_verify_nonce( $_GET['_wpnonce'], 'bp-resend-activation' ) ) {
2491          die( 'Security check' );
2492      }
2493  
2494      $signup_id = (int) $_GET['id'];
2495  
2496      // Resend the activation email.
2497      // also updates the 'last sent' and '# of emails sent' values.
2498      $resend = BP_Signup::resend( array( $signup_id ) );
2499  
2500      // Add feedback message.
2501      if ( ! empty( $resend['errors'] ) ) {
2502          $error = __( '<strong>Error</strong>: Your account has already been activated.', 'buddypress' );
2503      } else {
2504          $error = __( 'Activation email resent! Please check your inbox or spam folder.', 'buddypress' );
2505      }
2506  }
2507  add_action( 'login_form_bp-resend-activation', 'bp_members_login_resend_activation_email' );
2508  
2509  /**
2510   * Redirect away from wp-signup.php if BP registration templates are present.
2511   *
2512   * @since 1.1.0
2513   */
2514  function bp_core_wpsignup_redirect() {
2515  
2516      // Bail in admin or if custom signup page is broken.
2517      if ( is_admin() || ! bp_has_custom_signup_page() ) {
2518          return;
2519      }
2520  
2521      $is_wp_signup = false;
2522      if ( ! empty( $_SERVER['SCRIPT_NAME'] ) ) {
2523          $script_name_path = wp_parse_url( $_SERVER['SCRIPT_NAME'], PHP_URL_PATH );
2524  
2525          if ( 'wp-signup.php' === basename( $script_name_path ) || ( 'wp-login.php' === basename( $script_name_path ) && ! empty( $_GET['action'] ) && 'register' === $_GET['action'] ) ) {
2526              $is_wp_signup = true;
2527          }
2528      }
2529  
2530      // If this is not wp-signup.php, there's nothing to do here.
2531      if ( ! $is_wp_signup ) {
2532          return;
2533      }
2534  
2535      /*
2536       * We redirect wp-signup.php to the registration page except when it's a site signup.
2537       * In that case, redirect to the BP site creation page if available, otherwise allow
2538       * access to wp-signup.php.
2539       */
2540      $redirect_to = bp_get_signup_page();
2541  
2542      $is_site_creation = false;
2543  
2544      $referer = wp_get_referer();
2545  
2546      // A new site is being added.
2547      if ( isset( $_POST['stage'] ) && $_POST['stage'] === 'gimmeanotherblog' ) {
2548          $is_site_creation = true;
2549  
2550      // We've arrived at wp-signup.php from my-sites.php.
2551      } elseif ( $referer ) {
2552          $referer_path     = wp_parse_url( $referer, PHP_URL_PATH );
2553          $is_site_creation = false !== strpos( $referer_path, 'wp-admin/my-sites.php' );
2554      }
2555  
2556      if ( $is_site_creation ) {
2557          if ( bp_is_active( 'blogs' ) ) {
2558              $redirect_to = trailingslashit( bp_get_blogs_directory_permalink() . 'create' );
2559          } else {
2560              // Perform no redirect in this case.
2561              $redirect_to = '';
2562          }
2563      }
2564  
2565      if ( ! $redirect_to ) {
2566          return;
2567      }
2568  
2569      bp_core_redirect( $redirect_to );
2570  }
2571  add_action( 'bp_init', 'bp_core_wpsignup_redirect' );
2572  
2573  /**
2574   * Stop a logged-in user who is marked as a spammer.
2575   *
2576   * When an admin marks a live user as a spammer, that user can still surf
2577   * around and cause havoc on the site until that person is logged out.
2578   *
2579   * This code checks to see if a logged-in user is marked as a spammer.  If so,
2580   * we redirect the user back to wp-login.php with the 'reauth' parameter.
2581   *
2582   * This clears the logged-in spammer's cookies and will ask the spammer to
2583   * reauthenticate.
2584   *
2585   * Note: A spammer cannot log back in - {@see bp_core_boot_spammer()}.
2586   *
2587   * Runs on 'bp_init' at priority 5 so the members component globals are setup
2588   * before we do our spammer checks.
2589   *
2590   * This is important as the $bp->loggedin_user object is setup at priority 4.
2591   *
2592   * @since 1.8.0
2593   */
2594  function bp_stop_live_spammer() {
2595      // If we're on the login page, stop now to prevent redirect loop.
2596      $is_login = false;
2597      if ( isset( $GLOBALS['pagenow'] ) && ( false !== strpos( $GLOBALS['pagenow'], 'wp-login.php' ) ) ) {
2598          $is_login = true;
2599      } elseif ( isset( $_SERVER['SCRIPT_NAME'] ) && false !== strpos( $_SERVER['SCRIPT_NAME'], 'wp-login.php' ) ) {
2600          $is_login = true;
2601      }
2602  
2603      if ( $is_login ) {
2604          return;
2605      }
2606  
2607      // User isn't logged in, so stop!
2608      if ( ! is_user_logged_in() ) {
2609          return;
2610      }
2611  
2612      // If spammer, redirect to wp-login.php and reauthorize.
2613      if ( bp_is_user_spammer( bp_loggedin_user_id() ) ) {
2614          // Setup login args.
2615          $args = array(
2616              // Custom action used to throw an error message.
2617              'action' => 'bp-spam',
2618  
2619              // Reauthorize user to login.
2620              'reauth' => 1
2621          );
2622  
2623          /**
2624           * Filters the url used for redirection for a logged in user marked as spam.
2625           *
2626           * @since 1.8.0
2627           *
2628           * @param string $value URL to redirect user to.
2629           */
2630          $login_url = apply_filters( 'bp_live_spammer_redirect', add_query_arg( $args, wp_login_url() ) );
2631  
2632          // Redirect user to login page.
2633          wp_redirect( $login_url );
2634          die();
2635      }
2636  }
2637  add_action( 'bp_init', 'bp_stop_live_spammer', 5 );
2638  
2639  /**
2640   * Show a custom error message when a logged-in user is marked as a spammer.
2641   *
2642   * @since 1.8.0
2643   */
2644  function bp_live_spammer_login_error() {
2645      global $error;
2646  
2647      $error = __( '<strong>Error</strong>: Your account has been marked as a spammer.', 'buddypress' );
2648  
2649      // Shake shake shake!
2650      add_action( 'login_head', 'wp_shake_js', 12 );
2651  }
2652  add_action( 'login_form_bp-spam', 'bp_live_spammer_login_error' );
2653  
2654  /**
2655   * Get the displayed user Object
2656   *
2657   * @since 2.6.0
2658   *
2659   * @global BuddyPress $bp The one true BuddyPress instance.
2660   *
2661   * @return object The displayed user object, null otherwise.
2662   */
2663  function bp_get_displayed_user() {
2664      $bp = buddypress();
2665  
2666      $displayed_user = null;
2667      if ( ! empty( $bp->displayed_user->id ) ) {
2668          $displayed_user = $bp->displayed_user;
2669      }
2670  
2671      /**
2672       * Filters the displayed_user object corresponding to the displayed member.
2673       *
2674       * @since 2.6.0
2675       *
2676       * @param object $displayed_user The displayed_user object.
2677       */
2678      return apply_filters( 'bp_get_displayed_user', $displayed_user );
2679  }
2680  
2681  /** Member Types *************************************************************/
2682  
2683  /**
2684   * Output the slug of the member type taxonomy.
2685   *
2686   * @since 2.7.0
2687   */
2688  function bp_member_type_tax_name() {
2689      echo bp_get_member_type_tax_name();
2690  }
2691      /**
2692       * Return the slug of the member type taxonomy.
2693       *
2694       * @since 2.7.0
2695       *
2696       * @return string The unique member taxonomy slug.
2697       */
2698  	function bp_get_member_type_tax_name() {
2699  
2700          /**
2701           * Filters the slug of the member type taxonomy.
2702           *
2703           * @since 2.7.0
2704           *
2705           * @param string $value Member type taxonomy slug.
2706           */
2707          return apply_filters( 'bp_get_member_type_tax_name', 'bp_member_type' );
2708      }
2709  
2710  /**
2711   * Returns labels used by the member type taxonomy.
2712   *
2713   * @since 7.0.0
2714   *
2715   * @return array
2716   */
2717  function bp_get_member_type_tax_labels() {
2718  
2719      /**
2720       * Filters Member type taxonomy labels.
2721       *
2722       * @since 7.0.0
2723       *
2724       * @param array $value Associative array (name => label).
2725       */
2726      return apply_filters(
2727          'bp_get_member_type_tax_labels',
2728          array(
2729              // General labels.
2730              'name'                       => _x( 'Member Types', 'Member type taxonomy name', 'buddypress' ),
2731              'singular_name'              => _x( 'Member Type', 'Member type taxonomy singular name', 'buddypress' ),
2732              'search_items'               => _x( 'Search Member Types', 'Member type taxonomy search items label', 'buddypress' ),
2733              'popular_items'              => _x( 'Popular Member Types', 'Member type taxonomy popular items label', 'buddypress' ),
2734              'all_items'                  => _x( 'All Member Types', 'Member type taxonomy all items label', 'buddypress' ),
2735              'parent_item'                => _x( 'Parent Member Type', 'Member type taxonomy parent item label', 'buddypress' ),
2736              'parent_item_colon'          => _x( 'Parent Member Type:', 'Member type taxonomy parent item label', 'buddypress' ),
2737              'edit_item'                  => _x( 'Edit Member Type', 'Member type taxonomy edit item label', 'buddypress' ),
2738              'view_item'                  => _x( 'View Member Type', 'Member type taxonomy view item label', 'buddypress' ),
2739              'update_item'                => _x( 'Update Member Type', 'Member type taxonomy update item label', 'buddypress' ),
2740              'add_new_item'               => _x( 'Add New Member Type', 'Member type taxonomy add new item label', 'buddypress' ),
2741              'new_item_name'              => _x( 'New Member Type Name', 'Member type taxonomy new item name label', 'buddypress' ),
2742              'separate_items_with_commas' => _x( 'Separate member types with commas', 'Member type taxonomy separate items with commas label', 'buddypress' ),
2743              'add_or_remove_items'        => _x( 'Add or remove member types', 'Member type taxonomy add or remove items label', 'buddypress' ),
2744              'choose_from_most_used'      => _x( 'Choose from the most used meber types', 'Member type taxonomy choose from most used label', 'buddypress' ),
2745              'not_found'                  => _x( 'No member types found.', 'Member type taxonomy not found label', 'buddypress' ),
2746              'no_terms'                   => _x( 'No member types', 'Member type taxonomy no terms label', 'buddypress' ),
2747              'items_list_navigation'      => _x( 'Member Types list navigation', 'Member type taxonomy items list navigation label', 'buddypress' ),
2748              'items_list'                 => _x( 'Member Types list', 'Member type taxonomy items list label', 'buddypress' ),
2749  
2750              /* translators: Tab heading when selecting from the most used terms. */
2751              'most_used'                  => _x( 'Most Used', 'Member type taxonomy most used items label', 'buddypress' ),
2752              'back_to_items'              => _x( '&larr; Back to Member Types', 'Member type taxonomy back to items label', 'buddypress' ),
2753  
2754              // Specific to BuddyPress.
2755              'bp_type_id_label'           => _x( 'Member Type ID', 'BP Member type ID label', 'buddypress' ),
2756              'bp_type_id_description'     => _x( 'Enter a lower-case string without spaces or special characters (used internally to identify the member type).', 'BP Member type ID description', 'buddypress' ),
2757              'bp_type_show_in_list'       => _x( 'Show on Member', 'BP Member type show in list', 'buddypress' ),
2758          )
2759      );
2760  }
2761  
2762  /**
2763   * Returns arguments used by the Member type taxonomy.
2764   *
2765   * @since 7.0.0
2766   *
2767   * @return array
2768   */
2769  function bp_get_member_type_tax_args() {
2770  
2771      /**
2772       * Filters Member type taxonomy args.
2773       *
2774       * @since 7.0.0
2775       *
2776       * @param array $value Associative array (key => arg).
2777       */
2778      return apply_filters(
2779          'bp_get_member_type_tax_args',
2780          array_merge(
2781              array(
2782                  'description' => _x( 'BuddyPress Member Types', 'Member type taxonomy description', 'buddypress' ),
2783                  'labels'      => array_merge( bp_get_member_type_tax_labels(), bp_get_taxonomy_common_labels() ),
2784              ),
2785              bp_get_taxonomy_common_args()
2786          )
2787      );
2788  }
2789  
2790  /**
2791   * Extend generic Type metadata schema to match Member Type needs.
2792   *
2793   * @since 7.0.0
2794   *
2795   * @param array  $schema   The generic Type metadata schema.
2796   * @param string $taxonomy The taxonomy name the schema applies to.
2797   * @return array           The Member Type metadata schema.
2798   */
2799  function bp_get_member_type_metadata_schema( $schema = array(), $taxonomy = '' ) {
2800      if ( bp_get_member_type_tax_name() === $taxonomy ) {
2801  
2802          // Directory.
2803          if ( isset( $schema['bp_type_has_directory']['description'] ) ) {
2804              $schema['bp_type_has_directory']['description'] = __( 'Make a list of members matching this type available on the members directory.', 'buddypress' );
2805          }
2806  
2807          // Slug.
2808          if ( isset( $schema['bp_type_directory_slug']['description'] ) ) {
2809              $schema['bp_type_directory_slug']['description'] = __( 'Enter if you want the type slug to be different from its ID.', 'buddypress' );
2810          }
2811  
2812          // List.
2813          $schema['bp_type_show_in_list'] = array(
2814              'description'       => __( 'Show where member types may be listed, like in the member header.', 'buddypress' ),
2815              'type'              => 'boolean',
2816              'single'            => true,
2817              'sanitize_callback' => 'absint',
2818          );
2819      }
2820  
2821      return $schema;
2822  }
2823  add_filter( 'bp_get_type_metadata_schema', 'bp_get_member_type_metadata_schema', 1, 2 );
2824  
2825  /**
2826   * Registers the Member type metadata.
2827   *
2828   * @since 7.0.0
2829   */
2830  function bp_register_member_type_metadata() {
2831      $type_taxonomy = bp_get_member_type_tax_name();
2832  
2833      foreach ( bp_get_type_metadata_schema( false, $type_taxonomy ) as $meta_key => $meta_args ) {
2834          bp_register_type_meta( $type_taxonomy, $meta_key, $meta_args );
2835      }
2836  }
2837  add_action( 'bp_register_type_metadata', 'bp_register_member_type_metadata' );
2838  
2839  /**
2840   * Register a member type.
2841   *
2842   * @since 2.2.0
2843   *
2844   * @global BuddyPress $bp The one true BuddyPress instance.
2845   *
2846   * @param string $member_type Unique string identifier for the member type.
2847   * @param array  $args {
2848   *     Array of arguments describing the member type.
2849   *
2850   *     @type array       $labels {
2851   *         Array of labels to use in various parts of the interface.
2852   *
2853   *         @type string $name          Default name. Should typically be plural.
2854   *         @type string $singular_name Singular name.
2855   *     }
2856   *     @type bool|string $has_directory Whether the member type should have its own type-specific directory.
2857   *                                      Pass `true` to use the `$member_type` string as the type's slug.
2858   *                                      Pass a string to customize the slug. Pass `false` to disable.
2859   *                                      Default: true.
2860   *     @type bool        $show_in_list  Whether this member type should be shown in lists rendered by
2861   *                                      bp_member_type_list(). Default: false.
2862   *     @type bool        $code          Whether this member type is registered using code. Default: true.
2863   *     @type int         $db_id         The member type term ID. Default: 0.
2864   * }
2865   * @return object|WP_Error Member type object on success, WP_Error object on failure.
2866   */
2867  function bp_register_member_type( $member_type, $args = array() ) {
2868      $bp = buddypress();
2869  
2870      if ( isset( $bp->members->types[ $member_type ] ) ) {
2871          return new WP_Error( 'bp_member_type_exists', __( 'Member type already exists.', 'buddypress' ), $member_type );
2872      }
2873  
2874      $r = bp_parse_args(
2875          $args,
2876          array(
2877              'labels'        => array(),
2878              'has_directory' => true,
2879              'show_in_list'  => false,
2880              'code'          => true,
2881              'db_id'         => 0,
2882          ),
2883          'register_member_type'
2884      );
2885  
2886      $member_type = sanitize_key( $member_type );
2887  
2888      /**
2889       * Filters the list of illegal member type names.
2890       *
2891       * - 'any' is a special pseudo-type, representing items unassociated with any member type.
2892       * - 'null' is a special pseudo-type, representing users without any type.
2893       * - '_none' is used internally to denote an item that should not apply to any member types.
2894       *
2895       * @since 2.4.0
2896       *
2897       * @param array $illegal_names Array of illegal names.
2898       */
2899      $illegal_names = apply_filters( 'bp_member_type_illegal_names', array( 'any', 'null', '_none' ) );
2900      if ( in_array( $member_type, $illegal_names, true ) ) {
2901          return new WP_Error( 'bp_member_type_illegal_name', __( 'You may not register a member type with this name.', 'buddypress' ), $member_type );
2902      }
2903  
2904      // Store the post type name as data in the object (not just as the array key).
2905      $r['name'] = $member_type;
2906  
2907      // Make sure the relevant labels have been filled in.
2908      $default_name = isset( $r['labels']['name'] ) ? $r['labels']['name'] : ucfirst( $r['name'] );
2909      $r['labels'] = array_merge( array(
2910          'name'          => $default_name,
2911          'singular_name' => $default_name,
2912      ), $r['labels'] );
2913  
2914      // Directory slug.
2915      if ( $r['has_directory'] ) {
2916          // A string value is intepreted as the directory slug. Otherwise fall back on member type.
2917          if ( is_string( $r['has_directory'] ) ) {
2918              $directory_slug = $r['has_directory'];
2919          } else {
2920              $directory_slug = $member_type;
2921          }
2922  
2923          // Sanitize for use in URLs.
2924          $r['directory_slug'] = sanitize_title( $directory_slug );
2925          $r['has_directory']  = true;
2926      } else {
2927          $r['directory_slug'] = '';
2928          $r['has_directory']  = false;
2929      }
2930  
2931      // Show the list of member types on front-end (member header, for now).
2932      $r['show_in_list'] = (bool) $r['show_in_list'];
2933  
2934      $bp->members->types[ $member_type ] = $type = (object) $r;
2935  
2936      /**
2937       * Fires after a member type is registered.
2938       *
2939       * @since 2.2.0
2940       *
2941       * @param string $member_type Member type identifier.
2942       * @param object $type        Member type object.
2943       */
2944      do_action( 'bp_registered_member_type', $member_type, $type );
2945  
2946      return $type;
2947  }
2948  
2949  /**
2950   * Retrieve a member type object by name.
2951   *
2952   * @since 2.2.0
2953   *
2954   * @param string $member_type The name of the member type.
2955   * @return object|null A member type object or null if it doesn't exist.
2956   */
2957  function bp_get_member_type_object( $member_type ) {
2958      $types = bp_get_member_types( array(), 'objects' );
2959  
2960      if ( empty( $types[ $member_type ] ) ) {
2961          return null;
2962      }
2963  
2964      return $types[ $member_type ];
2965  }
2966  
2967  /**
2968   * Get a list of all registered member type objects.
2969   *
2970   * @since 2.2.0
2971   *
2972   * @global BuddyPress $bp The one true BuddyPress instance.
2973   *
2974   * @see bp_register_member_type() for accepted arguments.
2975   *
2976   * @param array|string $args     Optional. An array of key => value arguments to match against
2977   *                               the member type objects. Default empty array.
2978   * @param string       $output   Optional. The type of output to return. Accepts 'names'
2979   *                               or 'objects'. Default 'names'.
2980   * @param string       $operator Optional. The logical operation to perform. 'or' means only one
2981   *                               element from the array needs to match; 'and' means all elements
2982   *                               must match. Accepts 'or' or 'and'. Default 'and'.
2983   * @return array A list of member type names or objects.
2984   */
2985  function bp_get_member_types( $args = array(), $output = 'names', $operator = 'and' ) {
2986      $bp    = buddypress();
2987      $types = $bp->members->types;
2988  
2989      // Merge with types available into the database.
2990      if ( ! isset( $args['code'] ) || true !== $args['code'] ) {
2991          $types = bp_get_taxonomy_types( bp_get_member_type_tax_name(), $types );
2992      }
2993  
2994      $types = wp_filter_object_list( $types, $args, $operator );
2995  
2996      /**
2997       * Filters the array of member type objects.
2998       *
2999       * This filter is run before the $output filter has been applied, so that
3000       * filtering functions have access to the entire member type objects.
3001       *
3002       * @since 2.2.0
3003       *
3004       * @param array  $types     Member type objects, keyed by name.
3005       * @param array  $args      Array of key=>value arguments for filtering.
3006       * @param string $operator  'or' to match any of $args, 'and' to require all.
3007       */
3008      $types = apply_filters( 'bp_get_member_types', $types, $args, $operator );
3009  
3010      if ( 'names' === $output ) {
3011          $types = wp_list_pluck( $types, 'name' );
3012      }
3013  
3014      return $types;
3015  }
3016  
3017  /**
3018   * Only gets the member types registered by code.
3019   *
3020   * @since 7.0.0
3021   *
3022   * @return array The member types registered by code.
3023   */
3024  function bp_get_member_types_registered_by_code() {
3025      return bp_get_member_types(
3026          array(
3027              'code' => true,
3028          ),
3029          'objects'
3030      );
3031  }
3032  add_filter( bp_get_member_type_tax_name() . '_registered_by_code', 'bp_get_member_types_registered_by_code' );
3033  
3034  /**
3035   * Generates missing metadata for a type registered by code.
3036   *
3037   * @since 7.0.0
3038   *
3039   * @return array The member type metadata.
3040   */
3041  function bp_set_registered_by_code_member_type_metadata( $metadata = array(), $type = '' ) {
3042      $member_type = bp_get_member_type_object( $type );
3043  
3044      foreach ( get_object_vars( $member_type ) as $object_key => $object_value ) {
3045          if ( 'labels' === $object_key ) {
3046              foreach ( $object_value as $label_key => $label_value ) {
3047                  $metadata[ 'bp_type_' . $label_key ] = $label_value;
3048              }
3049          } elseif ( ! in_array( $object_key, array( 'name', 'code', 'db_id' ), true ) ) {
3050              $metadata[ 'bp_type_' . $object_key ] = $object_value;
3051          }
3052      }
3053  
3054      /**
3055       * Save metadata into database to avoid generating metadata
3056       * each time a type is listed into the Types Admin screen.
3057       */
3058      if ( isset( $member_type->db_id ) && $member_type->db_id ) {
3059          bp_update_type_metadata( $member_type->db_id, bp_get_member_type_tax_name(), $metadata );
3060      }
3061  
3062      return $metadata;
3063  }
3064  add_filter( bp_get_member_type_tax_name() . '_set_registered_by_code_metada', 'bp_set_registered_by_code_member_type_metadata', 10, 2 );
3065  
3066  /**
3067   * Insert member types registered by code not yet saved into the database as WP Terms.
3068   *
3069   * @since 7.0.0
3070   */
3071  function bp_insert_member_types_registered_by_code() {
3072      $all_types     = bp_get_member_types( array(), 'objects' );
3073      $unsaved_types = wp_filter_object_list( $all_types, array( 'db_id' => 0 ), 'and', 'name' );
3074  
3075      if ( $unsaved_types ) {
3076          foreach ( $unsaved_types as $type_name ) {
3077              bp_insert_term(
3078                  $type_name,
3079                  bp_get_member_type_tax_name(),
3080                  array(
3081                      'slug' => $type_name,
3082                  )
3083              );
3084          }
3085      }
3086  }
3087  add_action( bp_get_member_type_tax_name() . '_add_form', 'bp_insert_member_types_registered_by_code', 1 );
3088  
3089  /**
3090   * Set type for a member.
3091   *
3092   * @since 2.2.0
3093   * @since 7.0.0 $member_type parameter also accepts an array of member type names.
3094   *
3095   * @param int          $user_id     ID of the user.
3096   * @param string|array $member_type The member type name or an array of member type names.
3097   * @param bool         $append      Optional. True to append this to existing types for user,
3098   *                                  false to replace. Default: false.
3099   * @return bool|array $retval See {@see bp_set_object_terms()}.
3100   */
3101  function bp_set_member_type( $user_id, $member_type, $append = false ) {
3102      // Pass an empty $member_type to remove a user's type.
3103      if ( ! empty( $member_type ) ) {
3104          $member_types = (array) $member_type;
3105          $valid_types  = array_filter( array_map( 'bp_get_member_type_object', $member_types ) );
3106  
3107          if ( $valid_types ) {
3108              $member_type = wp_list_pluck( $valid_types, 'name' );
3109          } else {
3110              return false;
3111          }
3112      }
3113  
3114      $retval = bp_set_object_terms( $user_id, $member_type, bp_get_member_type_tax_name(), $append );
3115  
3116      // Bust the cache if the type has been updated.
3117      if ( ! is_wp_error( $retval ) ) {
3118          wp_cache_delete( $user_id, 'bp_member_member_type' );
3119  
3120          /**
3121           * Fires just after a user's member type has been changed.
3122           *
3123           * @since 2.2.0
3124           *
3125           * @param int          $user_id     ID of the user whose member type has been updated.
3126           * @param string|array $member_type The member type name or an array of member type names.
3127           * @param bool         $append      Whether the type is being appended to existing types.
3128           */
3129          do_action( 'bp_set_member_type', $user_id, $member_type, $append );
3130      }
3131  
3132      return $retval;
3133  }
3134  
3135  /**
3136   * Remove type for a member.
3137   *
3138   * @since 2.3.0
3139   *
3140   * @param int    $user_id     ID of the user.
3141   * @param string $member_type Member Type.
3142   * @return bool|WP_Error
3143   */
3144  function bp_remove_member_type( $user_id, $member_type ) {
3145      // Bail if no valid member type was passed.
3146      if ( empty( $member_type ) || ! bp_get_member_type_object( $member_type ) ) {
3147          return false;
3148      }
3149  
3150      // No need to continue if the member doesn't have the type.
3151      $existing_types = bp_get_member_type( $user_id, false );
3152      if ( ! in_array( $member_type, $existing_types, true ) ) {
3153          return false;
3154      }
3155  
3156      $deleted = bp_remove_object_terms( $user_id, $member_type, bp_get_member_type_tax_name() );
3157  
3158      // Bust the cache if the type has been removed.
3159      if ( ! is_wp_error( $deleted ) ) {
3160          wp_cache_delete( $user_id, 'bp_member_member_type' );
3161  
3162          /**
3163           * Fires just after a user's member type has been removed.
3164           *
3165           * @since 2.3.0
3166           *
3167           * @param int    $user_id     ID of the user whose member type has been updated.
3168           * @param string $member_type Member type.
3169           */
3170          do_action( 'bp_remove_member_type', $user_id, $member_type );
3171      }
3172  
3173      return $deleted;
3174  }
3175  
3176  /**
3177   * Get type for a member.
3178   *
3179   * @since 2.2.0
3180   * @since 7.0.0 Adds the `$use_db` parameter.
3181   *
3182   * @param int  $user_id ID of the user.
3183   * @param bool $single  Optional. Whether to return a single type string. If multiple types are found
3184   *                      for the user, the oldest one will be returned. Default: true.
3185   * @param bool $use_db  Optional. Whether to request all member types or only the ones registered by code.
3186   *                      Default: true.
3187   * @return string|array|bool On success, returns a single member type (if $single is true) or an array of member
3188   *                           types (if $single is false). Returns false on failure.
3189   */
3190  function bp_get_member_type( $user_id, $single = true, $use_db = true ) {
3191      $types = wp_cache_get( $user_id, 'bp_member_member_type' );
3192  
3193      if ( false === $types ) {
3194          $raw_types = bp_get_object_terms( $user_id, bp_get_member_type_tax_name() );
3195  
3196          if ( ! is_wp_error( $raw_types ) ) {
3197              $types = array();
3198  
3199              // Only include currently registered group types.
3200              foreach ( $raw_types as $mtype ) {
3201                  if ( bp_get_member_type_object( $mtype->name ) ) {
3202                      $types[] = $mtype->name;
3203                  }
3204              }
3205  
3206              wp_cache_set( $user_id, $types, 'bp_member_member_type' );
3207          }
3208      }
3209  
3210      if ( false === $use_db && $types ) {
3211          $registred_by_code = bp_get_member_types_registered_by_code();
3212          $ctype_names       = wp_list_pluck( $registred_by_code, 'name' );
3213          $types             = array_intersect( $types, $ctype_names );
3214      }
3215  
3216      $type = false;
3217      if ( ! empty( $types ) ) {
3218          if ( $single ) {
3219              $type = array_pop( $types );
3220          } else {
3221              $type = $types;
3222          }
3223      }
3224  
3225      /**
3226       * Filters a user's member type(s).
3227       *
3228       * @since 2.2.0
3229       *
3230       * @param string|array|bool $type    A single member type (if $single is true) or an array of member types
3231       *                                   (if $single is false) or false on failure.
3232       * @param int               $user_id ID of the user.
3233       * @param bool              $single  Whether to return a single type string, or an array.
3234       */
3235      return apply_filters( 'bp_get_member_type', $type, $user_id, $single );
3236  }
3237  
3238  /**
3239   * Check whether the given user has a certain member type.
3240   *
3241   * @since 2.3.0
3242   *
3243   * @param int    $user_id     $user_id ID of the user.
3244   * @param string $member_type Member Type.
3245   * @return bool Whether the user has the given member type.
3246   */
3247  function bp_has_member_type( $user_id, $member_type ) {
3248      // Bail if no valid member type was passed.
3249      if ( empty( $member_type ) || ! bp_get_member_type_object( $member_type ) ) {
3250          return false;
3251      }
3252  
3253      // Get all user's member types.
3254      $types = bp_get_member_type( $user_id, false );
3255  
3256      if ( ! is_array( $types ) ) {
3257          return false;
3258      }
3259  
3260      return in_array( $member_type, $types, true );
3261  }
3262  
3263  /**
3264   * Delete a user's member type when the user when the user is deleted.
3265   *
3266   * @since 2.2.0
3267   *
3268   * @param int $user_id ID of the user.
3269   * @return bool|array $value See {@see bp_set_member_type()}.
3270   */
3271  function bp_remove_member_type_on_user_delete( $user_id ) {
3272      return bp_set_member_type( $user_id, '' );
3273  }
3274  add_action( 'wpmu_delete_user', 'bp_remove_member_type_on_user_delete' );
3275  
3276  /**
3277   * Deletes user member type on the 'delete_user' hook.
3278   *
3279   * @since 6.0.0
3280   *
3281   * @param int $user_id The ID of the deleted user.
3282   */
3283  function bp_remove_member_type_on_delete_user( $user_id ) {
3284      if ( ! bp_remove_user_data_on_delete_user_hook( 'member_type', $user_id ) ) {
3285          return;
3286      }
3287  
3288      bp_remove_member_type_on_user_delete( $user_id );
3289  }
3290  add_action( 'delete_user', 'bp_remove_member_type_on_delete_user' );
3291  
3292  /**
3293   * Get the "current" member type, if one is provided, in member directories.
3294   *
3295   * @since 2.3.0
3296   *
3297   * @global BuddyPress $bp The one true BuddyPress instance.
3298   *
3299   * @return string
3300   */
3301  function bp_get_current_member_type() {
3302      $bp = buddypress();
3303  
3304      /**
3305       * Filters the "current" member type, if one is provided, in member directories.
3306       *
3307       * @since 2.3.0
3308       *
3309       * @param string $value "Current" member type.
3310       */
3311      return apply_filters( 'bp_get_current_member_type', $bp->current_member_type );
3312  }
3313  
3314  /**
3315   * Setup the avatar upload directory for a user.
3316   *
3317   * @since 6.0.0
3318   *
3319   * @param string $directory The root directory name. Optional.
3320   * @param int    $user_id   The user ID. Optional.
3321   * @return array Array containing the path, URL, and other helpful settings.
3322   */
3323  function bp_members_avatar_upload_dir( $directory = 'avatars', $user_id = 0 ) {
3324  
3325      // Use displayed user if no user ID was passed.
3326      if ( empty( $user_id ) ) {
3327          $user_id = bp_displayed_user_id();
3328      }
3329  
3330      // Failsafe against accidentally nooped $directory parameter.
3331      if ( empty( $directory ) ) {
3332          $directory = 'avatars';
3333      }
3334  
3335      $path      = bp_core_avatar_upload_path() . '/' . $directory . '/' . $user_id;
3336      $newbdir   = $path;
3337      $newurl    = bp_core_avatar_url() . '/' . $directory . '/' . $user_id;
3338      $newburl   = $newurl;
3339      $newsubdir = '/' . $directory . '/' . $user_id;
3340  
3341      /**
3342       * Filters the avatar upload directory for a user.
3343       *
3344       * @since 6.0.0
3345       *
3346       * @param array $value Array containing the path, URL, and other helpful settings.
3347       */
3348      return apply_filters( 'bp_members_avatar_upload_dir', array(
3349          'path'    => $path,
3350          'url'     => $newurl,
3351          'subdir'  => $newsubdir,
3352          'basedir' => $newbdir,
3353          'baseurl' => $newburl,
3354          'error'   => false,
3355      ) );
3356  }
3357  
3358  /**
3359   * Send welcome email on successful user activation.
3360   *
3361   * @since 8.0.0
3362   *
3363   * @param int $user_id The new user's ID.
3364   */
3365  function bp_send_welcome_email( $user_id = 0 ) {
3366      if ( ! $user_id ) {
3367          return;
3368      }
3369  
3370      $profile_url = bp_core_get_user_domain( $user_id );
3371  
3372      /**
3373       * Use this filter to add/edit/remove tokens to use for your welcome email.
3374       *
3375       * @since 8.0.0
3376       *
3377       * @param array $value   An array of BP Email tokens.
3378       * @param int   $user_id The user ID.
3379       */
3380      $welcome_tokens = apply_filters(
3381          'bp_send_welcome_email_tokens',
3382          array(
3383              'displayname'      => bp_core_get_user_displayname( $user_id ),
3384              'profile.url'      => $profile_url,
3385              'lostpassword.url' => wp_lostpassword_url( $profile_url ),
3386          ),
3387          $user_id
3388      );
3389  
3390      bp_send_email( 'core-user-activation', $user_id, array( 'tokens' => $welcome_tokens ) );
3391  }
3392  add_action( 'bp_core_activated_user', 'bp_send_welcome_email', 10, 1 );
3393  
3394  /**
3395   * Get invitations to the BP community filtered by arguments.
3396   *
3397   * @since 8.0.0
3398   *
3399   * @param array $args Invitation arguments. See BP_Invitation::get() for list.
3400   * @return array $invites Matching BP_Invitation objects.
3401   */
3402  function bp_members_invitations_get_invites( $args = array() ) {
3403      $invites_class = new BP_Members_Invitation_Manager();
3404      return $invites_class->get_invitations( $args );
3405  }
3406  
3407  /**
3408   * Check whether a user has sent any community invitations.
3409   *
3410   * @since 8.0.0
3411   *
3412   * @param int $user_id ID of user to check for invitations sent by.
3413   *                     Defaults to the current user's ID.
3414   *
3415   * @return bool $invites True if user has sent invites.
3416   */
3417  function bp_members_invitations_user_has_sent_invites( $user_id = 0 ) {
3418      if ( 0 === $user_id ) {
3419          $user_id = bp_loggedin_user_id();
3420          if ( ! $user_id ) {
3421              return false;
3422          }
3423      }
3424      $invites_class = new BP_Members_Invitation_Manager();
3425      $args = array(
3426          'inviter_id' => $user_id,
3427      );
3428      return (bool) $invites_class->invitation_exists( $args );
3429  }
3430  
3431  /**
3432   * Invite a user to a BP community.
3433   *
3434   * @since 8.0.0
3435   *
3436   * @param array|string $args {
3437   *     Array of arguments.
3438   *     @type int    $invitee_email Email address of the user being invited.
3439   *     @type int    $network_id    ID of the network to which the user is being invited.
3440   *     @type int    $inviter_id    Optional. ID of the inviting user. Default:
3441   *                                 ID of the logged-in user.
3442   *     @type string $date_modified Optional. Modified date for the invitation.
3443   *                                 Default: current date/time.
3444   *     @type string $content       Optional. Message to invitee.
3445   *     @type bool   $send_invite   Optional. Whether the invitation should be
3446   *                                 sent now. Default: false.
3447   * }
3448   * @return bool True on success, false on failure.
3449   */
3450  function bp_members_invitations_invite_user( $args = array() ) {
3451      $r = bp_parse_args(
3452          $args,
3453          array(
3454              'invitee_email' => '',
3455              'network_id'    => get_current_network_id(),
3456              'inviter_id'    => bp_loggedin_user_id(),
3457              'date_modified' => bp_core_current_time(),
3458              'content'       => '',
3459              'send_invite'   => 0,
3460          ),
3461          'members_invitations_invite_user'
3462      );
3463  
3464      $inv_args = array(
3465          'invitee_email' => $r['invitee_email'],
3466          'item_id'       => $r['network_id'],
3467          'inviter_id'    => $r['inviter_id'],
3468          'date_modified' => $r['date_modified'],
3469          'content'       => $r['content'],
3470          'send_invite'   => $r['send_invite'],
3471      );
3472  
3473      // Create the invitataion.
3474      $invites_class = new BP_Members_Invitation_Manager();
3475      $created       = $invites_class->add_invitation( $inv_args );
3476  
3477      /**
3478       * Fires after the creation of a new network invite.
3479       *
3480       * @since 8.0.0
3481       *
3482       * @param array    $r       Array of parsed arguments for the network invite.
3483       * @param int|bool $created The ID of the invitation or false if it couldn't be created.
3484       */
3485      do_action( 'bp_members_invitations_invite_user', $r, $created );
3486  
3487      return $created;
3488  }
3489  
3490  /**
3491   * Resend a membership invitation email by id.
3492   *
3493   * @since 8.0.0
3494   *
3495   * @param int $id ID of the invitation to resend.
3496   * @return bool True on success, false on failure.
3497   */
3498  function bp_members_invitation_resend_by_id( $id = 0 ) {
3499  
3500      // Find the invitation before resending it.
3501      $existing_invite = new BP_Invitation( $id );
3502      $invites_class   = new BP_Members_Invitation_Manager();
3503      $success         = $invites_class->send_invitation_by_id( $id );
3504  
3505      if ( ! $success ) {
3506          return $success;
3507      }
3508  
3509      /**
3510       * Fires after the re-sending of a network invite.
3511       *
3512       * @since 8.0.0
3513       *
3514       * @param BP_Invitation $existing_invite The invitation that was resent.
3515       */
3516      do_action( 'bp_members_invitations_resend_invitation', $existing_invite );
3517  
3518      return $success;
3519  }
3520  
3521  /**
3522   * Delete a membership invitation by id.
3523   *
3524   * @since 8.0.0
3525   *
3526   * @param int $id ID of the invitation to delete.
3527   * @return int|bool Number of rows deleted on success, false on failure.
3528   */
3529  function bp_members_invitations_delete_by_id( $id = 0 ) {
3530  
3531      // Find the invitation before deleting it.
3532      $existing_invite = new BP_Invitation( $id );
3533      $invites_class   = new BP_Members_Invitation_Manager();
3534      $success         = $invites_class->delete_by_id( $id );
3535  
3536      if ( ! $success ) {
3537          return $success;
3538      }
3539  
3540      // Run a different action depending on the status of the invite.
3541      if ( ! $existing_invite->invite_sent ) {
3542          /**
3543           * Fires after the deletion of an unsent community invite.
3544           *
3545           * @since 8.0.0
3546           *
3547           * @param BP_Invitation $existing_invite The invitation to be deleted.
3548           */
3549          do_action( 'bp_members_invitations_canceled_invitation', $existing_invite );
3550      } else if ( ! $existing_invite->accepted ) {
3551          /**
3552           * Fires after the deletion of a sent, but not yet accepted, community invite.
3553           *
3554           * @since 8.0.0
3555           *
3556           * @param BP_Invitation $existing_invite The invitation to be deleted.
3557           */
3558          do_action( 'bp_members_invitations_revoked_invitation', $existing_invite );
3559      } else {
3560          /**
3561           * Fires after the deletion of a sent and accepted community invite.
3562           *
3563           * @since 8.0.0
3564           *
3565           * @param BP_Invitation $existing_invite The invitation to be deleted.
3566           */
3567          do_action( 'bp_members_invitations_deleted_invitation', $existing_invite );
3568      }
3569  
3570      return $success;
3571  }
3572  
3573  /**
3574   * Delete a membership invitation.
3575   *
3576   * @since 8.0.0
3577   *
3578   * @param intring $args {
3579   *     Array of arguments.
3580   *     @type int|array $id            Id(s) of the invitation(s) to remove.
3581   *     @type int       $invitee_email Email address of the user being invited.
3582   *     @type int       $network_id    ID of the network to which the user is being invited.
3583   *     @type int       $inviter_id    ID of the inviting user.
3584   *     @type int       $accepted      Whether the invitation has been accepted yet.
3585   *     @type int       $invite_sent   Whether the invitation has been sent yet.
3586   * }
3587   * @return bool True if all were deleted.
3588   */
3589  function bp_members_invitations_delete_invites( $args = array() ) {
3590      $r = bp_parse_args(
3591          $args,
3592          array(
3593              'id'            => false,
3594              'invitee_email' => '',
3595              'network_id'    => get_current_network_id(),
3596              'inviter_id'    => null,
3597              'accepted'      => null,
3598              'invite_sent'   => null,
3599          ),
3600          'members_invitations_delete_invites'
3601      );
3602  
3603      $inv_args = array(
3604          'id'            => $r['id'],
3605          'invitee_email' => $r['invitee_email'],
3606          'item_id'       => $r['network_id'],
3607          'inviter_id'    => $r['inviter_id'],
3608          'accepted'      => $r['accepted'],
3609          'invite_sent'   => $r['invite_sent'],
3610      );
3611  
3612      // Find the invitation(s).
3613      $invites     = bp_members_invitations_get_invites( $inv_args );
3614      $total_count = count( $invites );
3615  
3616      // Loop through, deleting each invitation.
3617      $deleted = 0;
3618      foreach ( $invites as $invite ) {
3619          $success = bp_members_invitations_delete_by_id( $invite->id );
3620          if ( $success ) {
3621              $deleted++;
3622          }
3623      }
3624  
3625      return $deleted === $total_count;
3626  }
3627  
3628  /**
3629   * Get hash based on details of a membership invitation and the inviter.
3630   *
3631   * @since 8.0.0
3632   *
3633   * @param BP_Invitation $invitation Invitation to create hash from.
3634   *
3635   * @return string $hash Calculated sha1 hash.
3636   */
3637  function bp_members_invitations_get_hash( $invitation ) {
3638      $hash = false;
3639  
3640      if ( ! empty( $invitation->id ) ) {
3641          $inviter_ud = get_userdata( $invitation->inviter_id );
3642          if ( $inviter_ud ) {
3643              /*
3644               * Use some inviter details as part of the hash so that invitations from
3645               * users who are subsequently marked as spam will be invalidated.
3646               */
3647              $hash = wp_hash( "{$invitation->inviter_id}:{$invitation->invitee_email}:{$inviter_ud->user_status}:{$inviter_ud->user_registered}" );
3648          }
3649      }
3650  
3651      // If there's a problem, return a string that will change and thus fail.
3652      if ( ! $hash ) {
3653          $hash = wp_generate_password( 32, false );
3654      }
3655  
3656      /**
3657       * Filters the hash calculated by the invitation details.
3658       *
3659       * @since 8.0.0
3660       *
3661       * @param string        $hash       Calculated sha1 hash.
3662       * @param BP_Invitation $invitation Invitation hash was created from.
3663       */
3664      return apply_filters( 'bp_members_invitations_get_hash', $hash, $invitation );
3665  }
3666  
3667  /**
3668   * Get the current invitation specified by the $_GET parameters.
3669   *
3670   * @since 8.0.0
3671   *
3672   * @return BP_Invitation $invite Invitation specified by the $_GET parameters.
3673   */
3674  function bp_get_members_invitation_from_request() {
3675      $invites_class = new BP_Members_Invitation_Manager();
3676      $invite        = $invites_class->get_by_id( 0 );
3677  
3678      if ( bp_get_members_invitations_allowed() && ! empty( $_GET['inv'] ) ) {
3679          // Check to make sure the passed hash matches a calculated hash.
3680          $maybe_invite = $invites_class->get_by_id( absint( $_GET['inv'] ) );
3681          $hash         = bp_members_invitations_get_hash( $maybe_invite );
3682  
3683          if ( $_GET['ih'] === $hash ) {
3684              $invite = $maybe_invite;
3685          }
3686      }
3687  
3688      /**
3689       * Filters the invitation specified by the $_GET parameters.
3690       *
3691       * @since 8.0.0
3692       *
3693       * @param BP_Invitation $invite Invitation specified by the $_GET parameters.
3694       */
3695      return apply_filters( 'bp_get_members_invitation_from_request', $invite );
3696  }


Generated: Tue Oct 26 01:00:55 2021 Cross-referenced by PHPXref 0.7.1