[ Index ]

PHP Cross Reference of BuddyPress

title

Body

[close]

/src/bp-core/ -> bp-core-avatars.php (source)

   1  <?php
   2  /**
   3   * BuddyPress Avatars.
   4   *
   5   * @package BuddyPress
   6   * @subpackage Core
   7   * @since 1.0.0
   8   */
   9  
  10  // Exit if accessed directly.
  11  defined( 'ABSPATH' ) || exit;
  12  
  13  /**
  14   * Set up the constants we need for avatar support.
  15   *
  16   * @since 1.2.0
  17   */
  18  function bp_core_set_avatar_constants() {
  19  
  20      $bp = buddypress();
  21  
  22      if ( !defined( 'BP_AVATAR_THUMB_WIDTH' ) )
  23          define( 'BP_AVATAR_THUMB_WIDTH', 50 );
  24  
  25      if ( !defined( 'BP_AVATAR_THUMB_HEIGHT' ) )
  26          define( 'BP_AVATAR_THUMB_HEIGHT', 50 );
  27  
  28      if ( !defined( 'BP_AVATAR_FULL_WIDTH' ) )
  29          define( 'BP_AVATAR_FULL_WIDTH', 150 );
  30  
  31      if ( !defined( 'BP_AVATAR_FULL_HEIGHT' ) )
  32          define( 'BP_AVATAR_FULL_HEIGHT', 150 );
  33  
  34      if ( !defined( 'BP_AVATAR_ORIGINAL_MAX_WIDTH' ) )
  35          define( 'BP_AVATAR_ORIGINAL_MAX_WIDTH', 450 );
  36  
  37      if ( !defined( 'BP_AVATAR_ORIGINAL_MAX_FILESIZE' ) ) {
  38          define( 'BP_AVATAR_ORIGINAL_MAX_FILESIZE', bp_attachments_get_max_upload_file_size( 'avatar' ) );
  39      }
  40  
  41      if ( ! defined( 'BP_SHOW_AVATARS' ) ) {
  42          define( 'BP_SHOW_AVATARS', bp_get_option( 'show_avatars' ) );
  43      }
  44  }
  45  add_action( 'bp_init', 'bp_core_set_avatar_constants', 3 );
  46  
  47  /**
  48   * Set up global variables related to avatars.
  49   *
  50   * @since 1.5.0
  51   */
  52  function bp_core_set_avatar_globals() {
  53      $bp = buddypress();
  54  
  55      $bp->avatar        = new stdClass;
  56      $bp->avatar->thumb = new stdClass;
  57      $bp->avatar->full  = new stdClass;
  58  
  59      // Dimensions.
  60      $bp->avatar->thumb->width  = BP_AVATAR_THUMB_WIDTH;
  61      $bp->avatar->thumb->height = BP_AVATAR_THUMB_HEIGHT;
  62      $bp->avatar->full->width   = BP_AVATAR_FULL_WIDTH;
  63      $bp->avatar->full->height  = BP_AVATAR_FULL_HEIGHT;
  64  
  65      // Upload maximums.
  66      $bp->avatar->original_max_width    = BP_AVATAR_ORIGINAL_MAX_WIDTH;
  67      $bp->avatar->original_max_filesize = BP_AVATAR_ORIGINAL_MAX_FILESIZE;
  68  
  69      // Defaults.
  70      $bp->avatar->thumb->default = bp_core_avatar_default_thumb();
  71      $bp->avatar->full->default  = bp_core_avatar_default();
  72  
  73      // These have to be set on page load in order to avoid infinite filter loops at runtime.
  74      $bp->avatar->upload_path = bp_core_avatar_upload_path();
  75      $bp->avatar->url = bp_core_avatar_url();
  76  
  77      // Cache the root blog's show_avatars setting, to avoid unnecessary
  78      // calls to switch_to_blog().
  79      $bp->avatar->show_avatars = (bool) BP_SHOW_AVATARS;
  80  
  81      // Backpat for pre-1.5.
  82      if ( ! defined( 'BP_AVATAR_UPLOAD_PATH' ) )
  83          define( 'BP_AVATAR_UPLOAD_PATH', $bp->avatar->upload_path );
  84  
  85      // Backpat for pre-1.5.
  86      if ( ! defined( 'BP_AVATAR_URL' ) )
  87          define( 'BP_AVATAR_URL', $bp->avatar->url );
  88  
  89      /**
  90       * Fires at the end of the core avatar globals setup.
  91       *
  92       * @since 1.5.0
  93       */
  94      do_action( 'bp_core_set_avatar_globals' );
  95  }
  96  add_action( 'bp_setup_globals', 'bp_core_set_avatar_globals' );
  97  
  98  /**
  99   * Checks whether a given gravatar is one of the default ones.
 100   *
 101   * @since 8.0.0
 102   *
 103   * @param string $d The name of the default gravatar.
 104   * @return bool True if it's a default gravatar. False otherwise.
 105   */
 106  function bp_core_is_default_gravatar( $d = '' ) {
 107      if ( ! $d ) {
 108          return false;
 109      }
 110  
 111      /** this filter is documented in wp-admin/options-discussion.php */
 112      $gravatar_defaults = apply_filters(
 113          'avatar_defaults',
 114          array_fill_keys(
 115              array(
 116                  'mystery',
 117                  'blank',
 118                  'gravatar_default',
 119                  'identicon',
 120                  'wavatar',
 121                  'monsterid',
 122                  'retro',
 123              ),
 124              ''
 125          )
 126      );
 127  
 128      return isset( $gravatar_defaults[ $d ] );
 129  }
 130  
 131  /**
 132   * Get an avatar for a BuddyPress object.
 133   *
 134   * Supports avatars for users, groups, and blogs by default, but can be
 135   * extended to support custom components as well.
 136   *
 137   * This function gives precedence to locally-uploaded avatars. When a local
 138   * avatar is not found, Gravatar is queried. To disable Gravatar fallbacks
 139   * locally:
 140   *    add_filter( 'bp_core_fetch_avatar_no_grav', '__return_true' );
 141   *
 142   * @since 1.1.0
 143   * @since 2.4.0 Added 'extra_attr', 'scheme', 'rating' and 'force_default' for $args.
 144   *              These are inherited from WordPress 4.2.0. See {@link get_avatar()}.
 145   *
 146   * @param array|string $args {
 147   *     An array of arguments. All arguments are technically optional; some
 148   *     will, if not provided, be auto-detected by bp_core_fetch_avatar(). This
 149   *     auto-detection is described more below, when discussing specific
 150   *     arguments.
 151   *
 152   *     @type int|bool    $item_id    The numeric ID of the item for which you're requesting
 153   *                                   an avatar (eg, a user ID). If no 'item_id' is present,
 154   *                                   the function attempts to infer an ID from the 'object' + the
 155   *                                   current context: if 'object' is 'user' and the current page is a
 156   *                                   user page, 'item_id' will default to the displayed user ID; if
 157   *                                   'group' and on a group page, to the current group ID; if 'blog',
 158   *                                   to the current blog's ID. If no 'item_id' can be determined in
 159   *                                   this way, the function returns false. Default: false.
 160   *     @type string      $object     The kind of object for which you're getting an
 161   *                                   avatar. BuddyPress natively supports three options: 'user',
 162   *                                   'group', 'blog'; a plugin may register more.  Default: 'user'.
 163   *     @type string      $type       When a new avatar is uploaded to BP, 'thumb' and
 164   *                                   'full' versions are saved. This parameter specifies whether you'd
 165   *                                   like the 'full' or smaller 'thumb' avatar. Default: 'thumb'.
 166   *     @type string|bool $avatar_dir The name of the subdirectory where the
 167   *                                   requested avatar should be found. If no value is passed,
 168   *                                   'avatar_dir' is inferred from 'object': 'user' becomes 'avatars',
 169   *                                   'group' becomes 'group-avatars', 'blog' becomes 'blog-avatars'.
 170   *                                   Remember that this string denotes a subdirectory of BP's main
 171   *                                   avatar directory (usually based on {@link wp_upload_dir()}); it's a
 172   *                                   string like 'group-avatars' rather than the full directory path.
 173   *                                   Generally, it'll only be necessary to override the default value if
 174   *                                   storing avatars in a non-default location. Defaults to false
 175   *                                   (auto-detected).
 176   *     @type int|bool    $width      Requested avatar width. The unit is px. This value
 177   *                                   is used to build the 'width' attribute for the <img> element. If
 178   *                                   no value is passed, BP uses the global avatar width for this
 179   *                                   avatar type. Default: false (auto-detected).
 180   *     @type int|bool    $height     Requested avatar height. The unit is px. This
 181   *                                   value is used to build the 'height' attribute for the <img>
 182   *                                   element. If no value is passed, BP uses the global avatar height
 183   *                                   for this avatar type. Default: false (auto-detected).
 184   *     @type string      $class      The CSS class for the <img> element. Note that BP
 185   *                                   uses the 'avatar' class fairly extensively in its default styling,
 186   *                                   so if you plan to pass a custom value, consider appending it to
 187   *                                   'avatar' (eg 'avatar foo') rather than replacing it altogether.
 188   *                                   Default: 'avatar'.
 189   *     @type string|bool $css_id     The CSS id for the <img> element.
 190   *                                   Default: false.
 191   *     @type string      $title      The title attribute for the <img> element.
 192   *                                   Default: false.
 193   *     @type string      $alt        The alt attribute for the <img> element. In BP, this
 194   *                                   value is generally passed by the wrapper functions, where the data
 195   *                                   necessary for concatenating the string is at hand; see
 196   *                                   {@link bp_get_activity_avatar()} for an example. Default: ''.
 197   *     @type string|bool $email      An email to use in Gravatar queries. Unless
 198   *                                   otherwise configured, BP uses Gravatar as a fallback for avatars
 199   *                                   that are not provided locally. Gravatar's API requires using a hash
 200   *                                   of the user's email address; this argument provides it. If not
 201   *                                   provided, the function will infer it: for users, by getting the
 202   *                                   user's email from the database, for groups/blogs, by concatenating
 203   *                                   "{$item_id}-{$object}@{bp_get_root_domain()}". The user query adds
 204   *                                   overhead, so it's recommended that wrapper functions provide a
 205   *                                   value for 'email' when querying user IDs. Default: false.
 206   *     @type bool       $no_grav     Whether to disable the default Gravatar fallback.
 207   *                                   By default, BP will fall back on Gravatar when it cannot find a
 208   *                                   local avatar. In some cases, this may be undesirable, in which
 209   *                                   case 'no_grav' should be set to true. To disable Gravatar
 210   *                                   fallbacks globally, see the 'bp_core_fetch_avatar_no_grav' filter.
 211   *                                   Default: true for groups, otherwise false.
 212   *     @type bool       $html        Whether to return an <img> HTML element, vs a raw URL
 213   *                                   to an avatar. If false, <img>-specific arguments (like 'css_id')
 214   *                                   will be ignored. Default: true.
 215   *     @type string     $extra_attr  HTML attributes to insert in the IMG element. Not sanitized. Default: ''.
 216   *     @type string     $scheme      URL scheme to use. See set_url_scheme() for accepted values.
 217   *                                   Default null.
 218   *     @type string     $rating      What rating to display Gravatars for. Accepts 'G', 'PG', 'R', 'X'.
 219   *                                   Default is the value of the 'avatar_rating' option.
 220   *     @type bool       $force_default Used when creating the Gravatar URL. Whether to force the default
 221   *                                     image regardless if the Gravatar exists. Default: false.
 222   * }
 223   * @return string Formatted HTML <img> element, or raw avatar URL based on $html arg.
 224   */
 225  function bp_core_fetch_avatar( $args = '' ) {
 226      $bp = buddypress();
 227  
 228      // If avatars are disabled for the root site, obey that request and bail.
 229      if ( ! $bp->avatar || ! $bp->avatar->show_avatars ) {
 230          return;
 231      }
 232  
 233      // Set the default variables array and parse it against incoming $args array.
 234      $params = bp_parse_args(
 235          $args,
 236          array(
 237              'item_id'       => false,
 238              'object'        => 'user',
 239              'type'          => 'thumb',
 240              'avatar_dir'    => false,
 241              'width'         => false,
 242              'height'        => false,
 243              'class'         => 'avatar',
 244              'css_id'        => false,
 245              'alt'           => '',
 246              'email'         => false,
 247              'no_grav'       => null,
 248              'html'          => true,
 249              'title'         => '',
 250              'extra_attr'    => '',
 251              'scheme'        => null,
 252              'rating'        => get_option( 'avatar_rating' ),
 253              'force_default' => false,
 254          )
 255      );
 256  
 257      /* Set item_id ***********************************************************/
 258  
 259      if ( empty( $params['item_id'] ) ) {
 260  
 261          switch ( $params['object'] ) {
 262  
 263              case 'blog'  :
 264                  $params['item_id'] = get_current_blog_id();
 265                  break;
 266  
 267              case 'group' :
 268                  if ( bp_is_active( 'groups' ) ) {
 269                      $params['item_id'] = $bp->groups->current_group->id;
 270                  } else {
 271                      $params['item_id'] = false;
 272                  }
 273  
 274                  break;
 275  
 276              case 'user'  :
 277              default      :
 278                  $params['item_id'] = bp_displayed_user_id();
 279                  break;
 280          }
 281  
 282          /**
 283           * Filters the ID of the item being requested.
 284           *
 285           * @since 1.1.0
 286           *
 287           * @param string $value  ID of avatar item being requested.
 288           * @param string $value  Avatar type being requested.
 289           * @param array  $params Array of parameters for the request.
 290           */
 291          $params['item_id'] = apply_filters( 'bp_core_avatar_item_id', $params['item_id'], $params['object'], $params );
 292  
 293          if ( empty( $params['item_id'] ) ) {
 294              return false;
 295          }
 296      }
 297  
 298      /* Set avatar_dir ********************************************************/
 299  
 300      if ( empty( $params['avatar_dir'] ) ) {
 301  
 302          switch ( $params['object'] ) {
 303  
 304              case 'blog'  :
 305                  $params['avatar_dir'] = 'blog-avatars';
 306                  break;
 307  
 308              case 'group' :
 309                  if ( bp_is_active( 'groups' ) ) {
 310                      $params['avatar_dir'] = 'group-avatars';
 311                  } else {
 312                      $params['avatar_dir'] = false;
 313                  }
 314  
 315                  break;
 316  
 317              case 'user'  :
 318              default      :
 319                  $params['avatar_dir'] = 'avatars';
 320                  break;
 321          }
 322  
 323          /**
 324           * Filters the avatar directory to use.
 325           *
 326           * @since 1.1.0
 327           *
 328           * @param string $value  Name of the subdirectory where the requested avatar should be found.
 329           * @param string $value  Avatar type being requested.
 330           * @param array  $params Array of parameters for the request.
 331           */
 332          $params['avatar_dir'] = apply_filters( 'bp_core_avatar_dir', $params['avatar_dir'], $params['object'], $params );
 333  
 334          if ( empty( $params['avatar_dir'] ) ) {
 335              return false;
 336          }
 337      }
 338  
 339      /* <img> alt *************************************************************/
 340  
 341      if ( false !== strpos( $params['alt'], '%s' ) || false !== strpos( $params['alt'], '%1$s' ) ) {
 342  
 343          switch ( $params['object'] ) {
 344  
 345              case 'blog'  :
 346                  $item_name = get_blog_option( $params['item_id'], 'blogname' );
 347                  break;
 348  
 349              case 'group' :
 350                  $item_name = bp_get_group_name( groups_get_group( $params['item_id'] ) );
 351                  break;
 352  
 353              case 'user'  :
 354              default :
 355                  $item_name = bp_core_get_user_displayname( $params['item_id'] );
 356                  break;
 357          }
 358  
 359          /**
 360           * Filters the alt attribute value to be applied to avatar.
 361           *
 362           * @since 1.5.0
 363           *
 364           * @param string $value  alt to be applied to avatar.
 365           * @param string $value  ID of avatar item being requested.
 366           * @param string $value  Avatar type being requested.
 367           * @param array  $params Array of parameters for the request.
 368           */
 369          $item_name = apply_filters( 'bp_core_avatar_alt', $item_name, $params['item_id'], $params['object'], $params );
 370          $params['alt'] = sprintf( $params['alt'], $item_name );
 371      }
 372  
 373      /* Sanity Checks *********************************************************/
 374  
 375      // Get a fallback for the 'alt' parameter, create html output.
 376      if ( empty( $params['alt'] ) ) {
 377          $params['alt'] = __( 'Profile Photo', 'buddypress' );
 378      }
 379      $html_alt = ' alt="' . esc_attr( $params['alt'] ) . '"';
 380  
 381      // Filter image title and create html string.
 382      $html_title = '';
 383  
 384      /**
 385       * Filters the title attribute value to be applied to avatar.
 386       *
 387       * @since 1.5.0
 388       *
 389       * @param string $value  Title to be applied to avatar.
 390       * @param string $value  ID of avatar item being requested.
 391       * @param string $value  Avatar type being requested.
 392       * @param array  $params Array of parameters for the request.
 393       */
 394      $params['title'] = apply_filters( 'bp_core_avatar_title', $params['title'], $params['item_id'], $params['object'], $params );
 395  
 396      if ( ! empty( $params['title'] ) ) {
 397          $html_title = ' title="' . esc_attr( $params['title'] ) . '"';
 398      }
 399  
 400      // Extra attributes.
 401      $extra_attr = '';
 402      if ( ! empty( $params['extra_attr'] ) ) {
 403          $extra_attr = ' ' . $params['extra_attr'];
 404      }
 405  
 406      // Set CSS ID and create html string.
 407      $html_css_id = '';
 408  
 409      /**
 410       * Filters the ID attribute to be applied to avatar.
 411       *
 412       * @since 2.2.0
 413       *
 414       * @param string $value  ID to be applied to avatar.
 415       * @param string $value  ID of avatar item being requested.
 416       * @param string $value  Avatar type being requested.
 417       * @param array  $params Array of parameters for the request.
 418       */
 419      $params['css_id'] = apply_filters( 'bp_core_css_id', $params['css_id'], $params['item_id'], $params['object'], $params );
 420  
 421      if ( ! empty( $params['css_id'] ) ) {
 422          $html_css_id = ' id="' . esc_attr( $params['css_id'] ) . '"';
 423      }
 424  
 425      // Set image width.
 426      if ( false !== $params['width'] ) {
 427          // Width has been specified. No modification necessary.
 428      } elseif ( 'thumb' == $params['type'] ) {
 429          $params['width'] = bp_core_avatar_thumb_width();
 430      } else {
 431          $params['width'] = bp_core_avatar_full_width();
 432      }
 433      $html_width = ' width="' . $params['width'] . '"';
 434  
 435      // Set image height.
 436      if ( false !== $params['height'] ) {
 437          // Height has been specified. No modification necessary.
 438      } elseif ( 'thumb' == $params['type'] ) {
 439          $params['height'] = bp_core_avatar_thumb_height();
 440      } else {
 441          $params['height'] = bp_core_avatar_full_height();
 442      }
 443      $html_height = ' height="' . $params['height'] . '"';
 444  
 445      /**
 446       * Filters the classes to be applied to the avatar.
 447       *
 448       * @since 1.6.0
 449       *
 450       * @param array|string $value  Class(es) to be applied to the avatar.
 451       * @param string       $value  ID of the avatar item being requested.
 452       * @param string       $value  Avatar type being requested.
 453       * @param array        $params Array of parameters for the request.
 454       */
 455      $params['class'] = apply_filters( 'bp_core_avatar_class', $params['class'], $params['item_id'], $params['object'], $params );
 456  
 457      // Use an alias to leave the param unchanged.
 458      $avatar_classes = $params['class'];
 459      if ( ! is_array( $avatar_classes ) ) {
 460          $avatar_classes = explode( ' ', $avatar_classes );
 461      }
 462  
 463      // Merge classes.
 464      $avatar_classes = array_merge( $avatar_classes, array(
 465          $params['object'] . '-' . $params['item_id'] . '-avatar',
 466          'avatar-' . $params['width'],
 467      ) );
 468  
 469      // Sanitize each class.
 470      $avatar_classes = array_map( 'sanitize_html_class', $avatar_classes );
 471  
 472      // Populate the class attribute.
 473      $html_class = ' class="' . join( ' ', $avatar_classes ) . ' photo"';
 474  
 475      // Set img URL and DIR based on prepopulated constants.
 476      $avatar_loc        = new stdClass();
 477      $avatar_loc->path  = trailingslashit( bp_core_avatar_upload_path() );
 478      $avatar_loc->url   = trailingslashit( bp_core_avatar_url() );
 479  
 480      $avatar_loc->dir   = trailingslashit( $params['avatar_dir'] );
 481  
 482      /**
 483       * Filters the avatar folder directory URL.
 484       *
 485       * @since 1.1.0
 486       *
 487       * @param string $value Path to the avatar folder URL.
 488       * @param int    $value ID of the avatar item being requested.
 489       * @param string $value Avatar type being requested.
 490       * @param string $value Subdirectory where the requested avatar should be found.
 491       */
 492      $avatar_folder_url = apply_filters( 'bp_core_avatar_folder_url', ( $avatar_loc->url  . $avatar_loc->dir . $params['item_id'] ), $params['item_id'], $params['object'], $params['avatar_dir'] );
 493  
 494      /**
 495       * Filters the avatar folder directory path.
 496       *
 497       * @since 1.1.0
 498       *
 499       * @param string $value Path to the avatar folder directory.
 500       * @param int    $value ID of the avatar item being requested.
 501       * @param string $value Avatar type being requested.
 502       * @param string $value Subdirectory where the requested avatar should be found.
 503       */
 504      $avatar_folder_dir = apply_filters( 'bp_core_avatar_folder_dir', ( $avatar_loc->path . $avatar_loc->dir . $params['item_id'] ), $params['item_id'], $params['object'], $params['avatar_dir'] );
 505  
 506      /**
 507       * Look for uploaded avatar first. Use it if it exists.
 508       * Set the file names to search for, to select the full size
 509       * or thumbnail image.
 510       */
 511      $avatar_size              = ( 'full' == $params['type'] ) ? '-bpfull' : '-bpthumb';
 512      $legacy_user_avatar_name  = ( 'full' == $params['type'] ) ? '-avatar2' : '-avatar1';
 513      $legacy_group_avatar_name = ( 'full' == $params['type'] ) ? '-groupavatar-full' : '-groupavatar-thumb';
 514  
 515      // Check for directory.
 516      if ( ! $params['force_default'] && file_exists( $avatar_folder_dir ) ) {
 517  
 518          // Open directory.
 519          if ( $av_dir = opendir( $avatar_folder_dir ) ) {
 520  
 521              // Stash files in an array once to check for one that matches.
 522              $avatar_files = array();
 523              while ( false !== ( $avatar_file = readdir( $av_dir ) ) ) {
 524                  // Only add files to the array (skip directories).
 525                  if ( 2 < strlen( $avatar_file ) ) {
 526                      $avatar_files[] = $avatar_file;
 527                  }
 528              }
 529  
 530              // Check for array.
 531              if ( 0 < count( $avatar_files ) ) {
 532  
 533                  // Check for current avatar.
 534                  foreach( $avatar_files as $key => $value ) {
 535                      if ( strpos ( $value, $avatar_size )!== false ) {
 536                          $avatar_url = $avatar_folder_url . '/' . $avatar_files[$key];
 537                      }
 538                  }
 539  
 540                  // Legacy avatar check.
 541                  if ( !isset( $avatar_url ) ) {
 542                      foreach( $avatar_files as $key => $value ) {
 543                          if ( strpos ( $value, $legacy_user_avatar_name )!== false ) {
 544                              $avatar_url = $avatar_folder_url . '/' . $avatar_files[$key];
 545                          }
 546                      }
 547  
 548                      // Legacy group avatar check.
 549                      if ( !isset( $avatar_url ) ) {
 550                          foreach( $avatar_files as $key => $value ) {
 551                              if ( strpos ( $value, $legacy_group_avatar_name )!== false ) {
 552                                  $avatar_url = $avatar_folder_url . '/' . $avatar_files[$key];
 553                              }
 554                          }
 555                      }
 556                  }
 557              }
 558          }
 559  
 560          // Close the avatar directory.
 561          closedir( $av_dir );
 562  
 563          // If we found a locally uploaded avatar.
 564          if ( isset( $avatar_url ) ) {
 565              // Support custom scheme.
 566              $avatar_url = set_url_scheme( $avatar_url, $params['scheme'] );
 567  
 568              // Return it wrapped in an <img> element.
 569              if ( true === $params['html'] ) {
 570  
 571                  /**
 572                   * Filters an avatar URL wrapped in an <img> element.
 573                   *
 574                   * @since 1.1.0
 575                   *
 576                   * @param string $value             Full <img> element for an avatar.
 577                   * @param array  $params            Array of parameters for the request.
 578                   * @param string $value             ID of the item requested.
 579                   * @param string $value             Subdirectory where the requested avatar should be found.
 580                   * @param string $html_css_id       ID attribute for avatar.
 581                   * @param string $html_width        Width attribute for avatar.
 582                   * @param string $html_height       Height attribute for avatar.
 583                   * @param string $avatar_folder_url Avatar URL path.
 584                   * @param string $avatar_folder_dir Avatar DIR path.
 585                   */
 586                  return apply_filters( 'bp_core_fetch_avatar', '<img src="' . $avatar_url . '"' . $html_class . $html_css_id  . $html_width . $html_height . $html_alt . $html_title . $extra_attr . ' />', $params, $params['item_id'], $params['avatar_dir'], $html_css_id, $html_width, $html_height, $avatar_folder_url, $avatar_folder_dir );
 587  
 588              // ...or only the URL
 589              } else {
 590  
 591                  /**
 592                   * Filters a locally uploaded avatar URL.
 593                   *
 594                   * @since 1.2.5
 595                   *
 596                   * @param string $avatar_url URL for a locally uploaded avatar.
 597                   * @param array  $params     Array of parameters for the request.
 598                   */
 599                  return apply_filters( 'bp_core_fetch_avatar_url', $avatar_url, $params );
 600              }
 601          }
 602      }
 603  
 604      // By default, Gravatar is not pinged for groups.
 605      if ( null === $params['no_grav'] ) {
 606          $params['no_grav'] = 'group' === $params['object'];
 607      }
 608  
 609      /**
 610       * Filters whether or not to skip Gravatar check.
 611       *
 612       * @since 1.5.0
 613       *
 614       * @param bool  $value  Whether or not to skip Gravatar.
 615       * @param array $params Array of parameters for the avatar request.
 616       */
 617      if ( ! apply_filters( 'bp_core_fetch_avatar_no_grav', $params['no_grav'], $params ) ) {
 618  
 619          // Set gravatar type.
 620          if ( empty( $bp->grav_default->{$params['object']} ) ) {
 621              $default_grav = 'wavatar';
 622          } elseif ( 'mystery' == $bp->grav_default->{$params['object']} ) {
 623  
 624              /**
 625               * Filters the Mystery person avatar src value.
 626               *
 627               * @since 1.2.0
 628               *
 629               * @param string $value Avatar value.
 630               * @param string $value Width to display avatar at.
 631               */
 632              $default_grav = apply_filters( 'bp_core_mysteryman_src', 'mm', $params['width'] );
 633          } else {
 634              $default_grav = $bp->grav_default->{$params['object']};
 635          }
 636  
 637          // Set gravatar object.
 638          if ( empty( $params['email'] ) ) {
 639              if ( 'user' == $params['object'] ) {
 640                  $params['email'] = bp_core_get_user_email( $params['item_id'] );
 641              } elseif ( 'group' == $params['object'] || 'blog' == $params['object'] ) {
 642                  $params['email'] = $params['item_id'] . '-' . $params['object'] . '@' . bp_get_root_domain();
 643              }
 644          }
 645  
 646          /**
 647           * Filters the Gravatar email to use.
 648           *
 649           * @since 1.1.0
 650           *
 651           * @param string $value Email to use in Gravatar request.
 652           * @param string $value ID of the item being requested.
 653           * @param string $value Object type being requested.
 654           */
 655          $params['email'] = apply_filters( 'bp_core_gravatar_email', $params['email'], $params['item_id'], $params['object'] );
 656  
 657          /**
 658           * Filters the Gravatar URL host.
 659           *
 660           * @since 1.0.2
 661           *
 662           * @param string $value Gravatar URL host.
 663           */
 664          $gravatar = apply_filters( 'bp_gravatar_url', '//www.gravatar.com/avatar/' );
 665  
 666          // Append email hash to Gravatar.
 667          $gravatar .=  md5( strtolower( $params['email'] ) );
 668  
 669          // Main Gravatar URL args.
 670          $url_args = array(
 671              's' => $params['width']
 672          );
 673  
 674          // Custom Gravatar URL args.
 675          if ( ! empty( $params['force_default'] ) ) {
 676              $url_args['f'] = 'y';
 677              $url_args['d'] = $params['default'];
 678          }
 679          if ( ! empty( $params['rating'] ) ) {
 680              $url_args['r'] = strtolower( $params['rating'] );
 681          }
 682  
 683          /** This filter is documented in wp-includes/deprecated.php */
 684          $d = apply_filters_deprecated(
 685              'bp_core_avatar_default',
 686              array( $default_grav, $params ),
 687              '8.0.0',
 688              'bp_core_avatar_gravatar_default||bp_core_default_avatar',
 689              __( 'This filter was used for 2 different purposes. If your goal was to filter the default *Gravatar*, please use `bp_core_avatar_gravatar_default` instead. Otherwise, please use `bp_core_default_avatar` instead.', 'buddypress' )
 690          );
 691  
 692          if ( bp_core_is_default_gravatar( $d ) ) {
 693              $default_grav = $d;
 694          }
 695  
 696          /**
 697           * Filters the Gravatar "d" parameter.
 698           *
 699           * @since 2.6.0
 700           * @since 8.0.0 The name of the filter was changed to `bp_core_avatar_gravatar_default`.
 701           *
 702           * @param string $default_grav The avatar default.
 703           * @param array  $params       The avatar's data.
 704           */
 705          $default_grav = apply_filters( 'bp_core_avatar_gravatar_default', $default_grav, $params );
 706  
 707          // Only set default image if 'Gravatar Logo' is not requested.
 708          if ( ! $params['force_default'] && 'gravatar_default' !== $default_grav ) {
 709              $url_args['d'] = $default_grav;
 710          }
 711  
 712          // Set up the Gravatar URL.
 713          $gravatar = esc_url( add_query_arg(
 714              rawurlencode_deep( array_filter( $url_args ) ),
 715              $gravatar
 716          ) );
 717  
 718      // No avatar was found, and we've been told not to use a gravatar.
 719      } else {
 720  
 721          /**
 722           * Filters the avatar default when Gravatar is not used.
 723           *
 724           * This is a variable filter dependent on the avatar type being requested.
 725           *
 726           * @since 1.5.0
 727           *
 728           * @param string $value  Default avatar for non-gravatar requests.
 729           * @param array  $params Array of parameters for the avatar request.
 730           */
 731          $gravatar = apply_filters( 'bp_core_default_avatar_' . $params['object'], bp_core_avatar_default( 'local', $params ), $params );
 732      }
 733  
 734      if ( true === $params['html'] ) {
 735  
 736          /** This filter is documented in bp-core/bp-core-avatars.php */
 737          return apply_filters( 'bp_core_fetch_avatar', '<img src="' . $gravatar . '"' . $html_css_id . $html_class . $html_width . $html_height . $html_alt . $html_title . $extra_attr . ' />', $params, $params['item_id'], $params['avatar_dir'], $html_css_id, $html_width, $html_height, $avatar_folder_url, $avatar_folder_dir );
 738      } else {
 739  
 740          /** This filter is documented in bp-core/bp-core-avatars.php */
 741          return apply_filters( 'bp_core_fetch_avatar_url', $gravatar, $params );
 742      }
 743  }
 744  
 745  /**
 746   * Delete an existing avatar.
 747   *
 748   * @since 1.1.0
 749   *
 750   * @param array|string $args {
 751   *     Array of function parameters.
 752   *     @type bool|int    $item_id    ID of the item whose avatar you're deleting.
 753   *                                   Defaults to the current item of type $object.
 754   *     @type string      $object     Object type of the item whose avatar you're
 755   *                                   deleting. 'user', 'group', 'blog', or custom.
 756   *                                   Default: 'user'.
 757   *     @type bool|string $avatar_dir Subdirectory where avatar is located.
 758   *                                   Default: false, which falls back on the default location
 759   *                                   corresponding to the $object.
 760   * }
 761   * @return bool True on success, false on failure.
 762   */
 763  function bp_core_delete_existing_avatar( $args = '' ) {
 764  
 765      $defaults = array(
 766          'item_id'    => false,
 767          'object'     => 'user', // User OR group OR blog OR custom type (if you use filters).
 768          'avatar_dir' => false
 769      );
 770  
 771      $args = bp_parse_args(
 772          $args,
 773          $defaults
 774      );
 775  
 776      /**
 777       * Filters whether or not to handle deleting an existing avatar.
 778       *
 779       * If you want to override this function, make sure you return false.
 780       *
 781       * @since 2.5.1
 782       *
 783       * @param bool  $value Whether or not to delete the avatar.
 784       * @param array $args {
 785       *     Array of function parameters.
 786       *
 787       *     @type bool|int    $item_id    ID of the item whose avatar you're deleting.
 788       *                                   Defaults to the current item of type $object.
 789       *     @type string      $object     Object type of the item whose avatar you're
 790       *                                   deleting. 'user', 'group', 'blog', or custom.
 791       *                                   Default: 'user'.
 792       *     @type bool|string $avatar_dir Subdirectory where avatar is located.
 793       *                                   Default: false, which falls back on the default location
 794       *                                   corresponding to the $object.
 795       * }
 796       */
 797      if ( ! apply_filters( 'bp_core_pre_delete_existing_avatar', true, $args ) ) {
 798          return true;
 799      }
 800  
 801      if ( empty( $args['item_id'] ) ) {
 802          if ( 'user' === $args['object'] ) {
 803              $args['item_id'] = bp_displayed_user_id();
 804          } elseif ( 'group' === $args['object'] ) {
 805              $args['item_id'] = buddypress()->groups->current_group->id;
 806          } elseif ( 'blog' === $args['object'] ) {
 807              $args['item_id'] = get_current_blog_id();
 808          }
 809  
 810          /** This filter is documented in bp-core/bp-core-avatars.php */
 811          $item_id = apply_filters( 'bp_core_avatar_item_id', $args['item_id'], $args['object'] );
 812      } else {
 813          $item_id = $args['item_id'];
 814      }
 815  
 816      if ( $item_id && ( ctype_digit( $item_id ) || is_int( $item_id ) ) ) {
 817          $item_id = (int) $item_id;
 818      } else {
 819          return false;
 820      }
 821  
 822      if ( empty( $args['avatar_dir'] ) ) {
 823          if ( 'user' === $args['object'] ) {
 824              $args['avatar_dir'] = 'avatars';
 825          } elseif ( 'group' === $args['object'] ) {
 826              $args['avatar_dir'] = 'group-avatars';
 827          } elseif ( 'blog' === $args['object'] ) {
 828              $args['avatar_dir'] = 'blog-avatars';
 829          }
 830  
 831          /** This filter is documented in bp-core/bp-core-avatars.php */
 832          $avatar_dir = apply_filters( 'bp_core_avatar_dir', $args['avatar_dir'], $args['object'] );
 833      } else {
 834          $avatar_dir = $args['avatar_dir'];
 835      }
 836  
 837      if ( ! $avatar_dir ) {
 838          return false;
 839      }
 840  
 841      /** This filter is documented in bp-core/bp-core-avatars.php */
 842      $avatar_folder_dir = apply_filters( 'bp_core_avatar_folder_dir', bp_core_avatar_upload_path() . '/' . $avatar_dir . '/' . $item_id, $item_id, $args['object'], $avatar_dir );
 843  
 844      if ( ! is_dir( $avatar_folder_dir ) ) {
 845          return false;
 846      }
 847  
 848      if ( $av_dir = opendir( $avatar_folder_dir ) ) {
 849          while ( false !== ( $avatar_file = readdir( $av_dir ) ) ) {
 850              if ( ( preg_match( "/-bpfull/", $avatar_file ) || preg_match( "/-bpthumb/", $avatar_file ) ) && '.' != $avatar_file && '..' != $avatar_file ) {
 851                  @unlink( $avatar_folder_dir . '/' . $avatar_file );
 852              }
 853          }
 854      }
 855      closedir( $av_dir );
 856  
 857      @rmdir( $avatar_folder_dir );
 858  
 859      /**
 860       * Fires after deleting an existing avatar.
 861       *
 862       * @since 1.1.0
 863       *
 864       * @param array $args Array of arguments used for avatar deletion.
 865       */
 866      do_action( 'bp_core_delete_existing_avatar', $args );
 867  
 868      return true;
 869  }
 870  
 871  /**
 872   * Ajax delete an avatar for a given object and item id.
 873   *
 874   * @since 2.3.0
 875   *
 876   * @return string|null A JSON object containing success data if the avatar was deleted,
 877   *                     error message otherwise.
 878   */
 879  function bp_avatar_ajax_delete() {
 880      if ( ! bp_is_post_request() ) {
 881          wp_send_json_error();
 882      }
 883  
 884      $avatar_data = $_POST;
 885  
 886      if ( empty( $avatar_data['object'] ) || empty( $avatar_data['item_id'] ) ) {
 887          wp_send_json_error();
 888      }
 889  
 890      $nonce = 'bp_delete_avatar_link';
 891      if ( 'group' === $avatar_data['object'] ) {
 892          $nonce = 'bp_group_avatar_delete';
 893      }
 894  
 895      // Check the nonce.
 896      check_admin_referer( $nonce, 'nonce' );
 897  
 898      // Capability check.
 899      if ( ! bp_attachments_current_user_can( 'edit_avatar', $avatar_data ) ) {
 900          wp_send_json_error();
 901      }
 902  
 903      // Handle delete.
 904      if ( bp_core_delete_existing_avatar( array( 'item_id' => $avatar_data['item_id'], 'object' => $avatar_data['object'] ) ) ) {
 905          $return = array(
 906              'avatar' => esc_url( bp_core_fetch_avatar( array(
 907                  'object'  => $avatar_data['object'],
 908                  'item_id' => $avatar_data['item_id'],
 909                  'html'    => false,
 910                  'type'    => 'full',
 911              ) ) ),
 912              'feedback_code' => 4,
 913              'item_id'       => $avatar_data['item_id'],
 914          );
 915  
 916          wp_send_json_success( $return );
 917      } else {
 918          wp_send_json_error( array(
 919              'feedback_code' => 3,
 920          ) );
 921      }
 922  }
 923  add_action( 'wp_ajax_bp_avatar_delete', 'bp_avatar_ajax_delete' );
 924  
 925  /**
 926   * Handle avatar uploading.
 927   *
 928   * The functions starts off by checking that the file has been uploaded
 929   * properly using bp_core_check_avatar_upload(). It then checks that the file
 930   * size is within limits, and that it has an accepted file extension (jpg, gif,
 931   * png). If everything checks out, crop the image and move it to its real
 932   * location.
 933   *
 934   * @since 1.1.0
 935   *
 936   * @see bp_core_check_avatar_upload()
 937   * @see bp_core_check_avatar_type()
 938   *
 939   * @param array  $file              The appropriate entry the from $_FILES superglobal.
 940   * @param string $upload_dir_filter A filter to be applied to 'upload_dir'.
 941   * @return bool True on success, false on failure.
 942   */
 943  function bp_core_avatar_handle_upload( $file, $upload_dir_filter ) {
 944  
 945      /**
 946       * Filters whether or not to handle uploading.
 947       *
 948       * If you want to override this function, make sure you return false.
 949       *
 950       * @since 1.2.4
 951       *
 952       * @param bool   $value             Whether or not to crop.
 953       * @param array  $file              Appropriate entry from $_FILES superglobal.
 954       * @parma string $upload_dir_filter A filter to be applied to 'upload_dir'.
 955       */
 956      if ( ! apply_filters( 'bp_core_pre_avatar_handle_upload', true, $file, $upload_dir_filter ) ) {
 957          return true;
 958      }
 959  
 960      // Setup some variables.
 961      $bp          = buddypress();
 962      $upload_path = bp_core_avatar_upload_path();
 963  
 964      // Upload the file.
 965      $avatar_attachment = new BP_Attachment_Avatar();
 966      $bp->avatar_admin->original = $avatar_attachment->upload( $file, $upload_dir_filter );
 967  
 968      // In case of an error, stop the process and display a feedback to the user.
 969      if ( ! empty( $bp->avatar_admin->original['error'] ) ) {
 970          /* translators: %s: the upload error message */
 971          bp_core_add_message( sprintf( __( 'Upload Failed! Error was: %s', 'buddypress' ), $bp->avatar_admin->original['error'] ), 'error' );
 972          return false;
 973      }
 974  
 975      // The Avatar UI available width.
 976      $ui_available_width = 0;
 977  
 978      // Try to set the ui_available_width using the avatar_admin global.
 979      if ( isset( $bp->avatar_admin->ui_available_width ) ) {
 980          $ui_available_width = $bp->avatar_admin->ui_available_width;
 981      }
 982  
 983      // Maybe resize.
 984      $original_file_size        = $avatar_attachment->get_image_data( $bp->avatar_admin->original['file'] );
 985      $bp->avatar_admin->resized = $avatar_attachment->shrink( $bp->avatar_admin->original['file'], $ui_available_width );
 986      $bp->avatar_admin->image   = new stdClass();
 987  
 988      // We only want to handle one image after resize.
 989      if ( empty( $bp->avatar_admin->resized ) || is_wp_error( $bp->avatar_admin->resized ) ) {
 990          $bp->avatar_admin->image->file = $bp->avatar_admin->original['file'];
 991          $bp->avatar_admin->image->dir  = str_replace( $upload_path, '', $bp->avatar_admin->original['file'] );
 992      } else {
 993          $bp->avatar_admin->image->file = $bp->avatar_admin->resized['path'];
 994          $bp->avatar_admin->image->dir  = str_replace( $upload_path, '', $bp->avatar_admin->resized['path'] );
 995          @unlink( $bp->avatar_admin->original['file'] );
 996      }
 997  
 998      // Check for WP_Error on what should be an image.
 999      if ( is_wp_error( $bp->avatar_admin->image->dir ) ) {
1000          /* translators: %s: the upload error message */
1001          bp_core_add_message( sprintf( __( 'Upload failed! Error was: %s', 'buddypress' ), $bp->avatar_admin->image->dir->get_error_message() ), 'error' );
1002          return false;
1003      }
1004  
1005      // If the uploaded image is smaller than the "full" dimensions, throw a warning.
1006      if ( $avatar_attachment->is_too_small( $bp->avatar_admin->image->file ) ) {
1007          if ( isset( $original_file_size['width'] ) && $original_file_size['width'] > bp_core_avatar_full_width() ) {
1008              $aspect_ratio = number_format_i18n( bp_core_avatar_full_width() / bp_core_avatar_full_height(), 2 );
1009  
1010              /* translators: %s: the value of the aspect ratio. */
1011              bp_core_add_message( sprintf( __( 'The aspect ratio of the image you selected is too great compared to the profile photo one. For best results, upload a picture having an aspect ratio closer to %s.', 'buddypress' ), $aspect_ratio ), 'error' );
1012          } else {
1013  
1014              /* translators: 1: the advised width size in pixels. 2: the advised height size in pixels. */
1015              bp_core_add_message( sprintf( __( 'You have selected an image that is smaller than recommended. For best results, upload a picture larger than %1$d x %2$d pixels.', 'buddypress' ), bp_core_avatar_full_width(), bp_core_avatar_full_height() ), 'error' );
1016          }
1017      }
1018  
1019      // Set the url value for the image.
1020      $bp->avatar_admin->image->url = bp_core_avatar_url() . $bp->avatar_admin->image->dir;
1021  
1022      return true;
1023  }
1024  
1025  /**
1026   * Ajax upload an avatar.
1027   *
1028   * @since 2.3.0
1029   *
1030   * @return string|null A JSON object containing success data if the upload succeeded
1031   *                     error message otherwise.
1032   */
1033  function bp_avatar_ajax_upload() {
1034      if ( ! bp_is_post_request() ) {
1035          wp_die();
1036      }
1037  
1038      /**
1039       * Sending the json response will be different if
1040       * the current Plupload runtime is html4.
1041       */
1042      $is_html4 = false;
1043      if ( ! empty( $_POST['html4' ] ) ) {
1044          $is_html4 = true;
1045      }
1046  
1047      // Check the nonce.
1048      check_admin_referer( 'bp-uploader' );
1049  
1050      // Init the BuddyPress parameters.
1051      $bp_params = array();
1052  
1053      // We need it to carry on.
1054      if ( ! empty( $_POST['bp_params' ] ) ) {
1055          $bp_params = $_POST['bp_params' ];
1056      } else {
1057          bp_attachments_json_response( false, $is_html4 );
1058      }
1059  
1060      // We need the object to set the uploads dir filter.
1061      if ( empty( $bp_params['object'] ) ) {
1062          bp_attachments_json_response( false, $is_html4 );
1063      }
1064  
1065      // Capability check.
1066      if ( ! bp_attachments_current_user_can( 'edit_avatar', $bp_params ) ) {
1067          bp_attachments_json_response( false, $is_html4 );
1068      }
1069  
1070      $bp = buddypress();
1071      $bp_params['upload_dir_filter'] = '';
1072      $needs_reset = array();
1073  
1074      if ( 'user' === $bp_params['object'] && bp_is_active( 'members' ) ) {
1075          $bp_params['upload_dir_filter'] = 'bp_members_avatar_upload_dir';
1076  
1077          if ( ! bp_displayed_user_id() && ! empty( $bp_params['item_id'] ) ) {
1078              $needs_reset = array( 'key' => 'displayed_user', 'value' => $bp->displayed_user );
1079              $bp->displayed_user->id = $bp_params['item_id'];
1080          }
1081      } elseif ( 'group' === $bp_params['object'] && bp_is_active( 'groups' ) ) {
1082          $bp_params['upload_dir_filter'] = 'groups_avatar_upload_dir';
1083  
1084          if ( ! bp_get_current_group_id() && ! empty( $bp_params['item_id'] ) ) {
1085              $needs_reset = array( 'component' => 'groups', 'key' => 'current_group', 'value' => $bp->groups->current_group );
1086              $bp->groups->current_group = groups_get_group( $bp_params['item_id'] );
1087          }
1088      } else {
1089          /**
1090           * Filter here to deal with other components.
1091           *
1092           * @since 2.3.0
1093           *
1094           * @var array $bp_params the BuddyPress Ajax parameters.
1095           */
1096          $bp_params = apply_filters( 'bp_core_avatar_ajax_upload_params', $bp_params );
1097      }
1098  
1099      if ( ! isset( $bp->avatar_admin ) ) {
1100          $bp->avatar_admin = new stdClass();
1101      }
1102  
1103      /**
1104       * The BuddyPress upload parameters is including the Avatar UI Available width,
1105       * add it to the avatar_admin global for a later use.
1106       */
1107      if ( isset( $bp_params['ui_available_width'] ) ) {
1108          $bp->avatar_admin->ui_available_width =  (int) $bp_params['ui_available_width'];
1109      }
1110  
1111      // Upload the avatar.
1112      $avatar = bp_core_avatar_handle_upload( $_FILES, $bp_params['upload_dir_filter'] );
1113  
1114      // Reset objects.
1115      if ( ! empty( $needs_reset ) ) {
1116          if ( ! empty( $needs_reset['component'] ) ) {
1117              $bp->{$needs_reset['component']}->{$needs_reset['key']} = $needs_reset['value'];
1118          } else {
1119              $bp->{$needs_reset['key']} = $needs_reset['value'];
1120          }
1121      }
1122  
1123      // Init the feedback message.
1124      $feedback_message = false;
1125  
1126      if ( ! empty( $bp->template_message ) ) {
1127          $feedback_message = $bp->template_message;
1128  
1129          // Remove template message.
1130          $bp->template_message      = false;
1131          $bp->template_message_type = false;
1132  
1133          @setcookie( 'bp-message', false, time() - 1000, COOKIEPATH, COOKIE_DOMAIN, is_ssl() );
1134          @setcookie( 'bp-message-type', false, time() - 1000, COOKIEPATH, COOKIE_DOMAIN, is_ssl() );
1135      }
1136  
1137      if ( empty( $avatar ) ) {
1138          // Default upload error.
1139          $message = __( 'Upload failed.', 'buddypress' );
1140  
1141          // Use the template message if set.
1142          if ( ! empty( $feedback_message ) ) {
1143              $message = $feedback_message;
1144          }
1145  
1146          // Upload error reply.
1147          bp_attachments_json_response( false, $is_html4, array(
1148              'type'    => 'upload_error',
1149              'message' => $message,
1150          ) );
1151      }
1152  
1153      if ( empty( $bp->avatar_admin->image->file ) ) {
1154          bp_attachments_json_response( false, $is_html4 );
1155      }
1156  
1157      $uploaded_image = @getimagesize( $bp->avatar_admin->image->file );
1158  
1159      // Set the name of the file.
1160      $name = $_FILES['file']['name'];
1161      $name_parts = pathinfo( $name );
1162      $name = trim( substr( $name, 0, - ( 1 + strlen( $name_parts['extension'] ) ) ) );
1163  
1164      // Finally return the avatar to the editor.
1165      bp_attachments_json_response( true, $is_html4, array(
1166          'name'      => $name,
1167          'url'       => $bp->avatar_admin->image->url,
1168          'width'     => $uploaded_image[0],
1169          'height'    => $uploaded_image[1],
1170          'feedback'  => $feedback_message,
1171      ) );
1172  }
1173  add_action( 'wp_ajax_bp_avatar_upload', 'bp_avatar_ajax_upload' );
1174  
1175  /**
1176   * Handle avatar webcam capture.
1177   *
1178   * @since 2.3.0
1179   * @since 10.0.0 Adds the `$return` param to eventually return the crop result.
1180   *
1181   * @param string $data    Base64 encoded image.
1182   * @param int    $item_id Item to associate.
1183   * @param string $return  Whether to get the crop `array` or a `boolean`. Defaults to `boolean`.
1184   * @return array|bool True on success, false on failure.
1185   */
1186  function bp_avatar_handle_capture( $data = '', $item_id = 0, $return = 'boolean' ) {
1187      if ( empty( $data ) || empty( $item_id ) ) {
1188          return false;
1189      }
1190  
1191      /**
1192       * Filters whether or not to handle avatar webcam capture.
1193       *
1194       * If you want to override this function, make sure you return false.
1195       *
1196       * @since 2.5.1
1197       *
1198       * @param bool   $value   Whether or not to crop.
1199       * @param string $data    Base64 encoded image.
1200       * @param int    $item_id Item to associate.
1201       */
1202      if ( ! apply_filters( 'bp_avatar_pre_handle_capture', true, $data, $item_id ) ) {
1203          return true;
1204      }
1205  
1206      $avatar_dir = bp_core_avatar_upload_path() . '/avatars';
1207  
1208      // It's not a regular upload, we may need to create this folder.
1209      if ( ! file_exists( $avatar_dir ) ) {
1210          if ( ! wp_mkdir_p( $avatar_dir ) ) {
1211              return false;
1212          }
1213      }
1214  
1215      /**
1216       * Filters the Avatar folder directory.
1217       *
1218       * @since 2.3.0
1219       *
1220       * @param string $avatar_dir Directory for storing avatars.
1221       * @param int    $item_id    ID of the item being acted on.
1222       * @param string $value      Avatar type.
1223       * @param string $value      Avatars word.
1224       */
1225      $avatar_folder_dir = apply_filters( 'bp_core_avatar_folder_dir', $avatar_dir . '/' . $item_id, $item_id, 'user', 'avatars' );
1226  
1227      // It's not a regular upload, we may need to create this folder.
1228      if( ! is_dir( $avatar_folder_dir ) ) {
1229          if ( ! wp_mkdir_p( $avatar_folder_dir ) ) {
1230              return false;
1231          }
1232      }
1233  
1234      $original_file = $avatar_folder_dir . '/webcam-capture-' . $item_id . '.png';
1235  
1236      if ( file_put_contents( $original_file, $data ) ) {
1237          $avatar_to_crop = str_replace( bp_core_avatar_upload_path(), '', $original_file );
1238  
1239          // Crop to default values.
1240          $crop_args = array( 'item_id' => $item_id, 'original_file' => $avatar_to_crop, 'crop_x' => 0, 'crop_y' => 0 );
1241  
1242          if ( 'array' === $return ) {
1243              return bp_core_avatar_handle_crop( $crop_args, 'array' );
1244          }
1245  
1246          return bp_core_avatar_handle_crop( $crop_args );
1247      } else {
1248          return false;
1249      }
1250  }
1251  
1252  /**
1253   * Crop an uploaded avatar.
1254   *
1255   * @since 1.1.0
1256   * @since 10.0.0 Adds the `$return` param to eventually return the crop result.
1257   *
1258   * @param array|string $args {
1259   *     Array of function parameters.
1260   *
1261   *     @type string      $object        Object type of the item whose avatar you're
1262   *                                      handling. 'user', 'group', 'blog', or custom.
1263   *                                      Default: 'user'.
1264   *     @type string      $avatar_dir    Subdirectory where avatar should be stored.
1265   *                                      Default: 'avatars'.
1266   *     @type bool|int    $item_id       ID of the item that the avatar belongs to.
1267   *     @type bool|string $original_file Absolute path to the original avatar file.
1268   *     @type int         $crop_w        Crop width. Default: the global 'full' avatar width,
1269   *                                      as retrieved by bp_core_avatar_full_width().
1270   *     @type int         $crop_h        Crop height. Default: the global 'full' avatar height,
1271   *                                      as retrieved by bp_core_avatar_full_height().
1272   *     @type int         $crop_x        The horizontal starting point of the crop. Default: 0.
1273   *     @type int         $crop_y        The vertical starting point of the crop. Default: 0.
1274   * }
1275   * @param string       $return Whether to get the crop `array` or a `boolean`. Defaults to `boolean`.
1276   * @return array|bool True or the crop result on success, false on failure.
1277   */
1278  function bp_core_avatar_handle_crop( $args = '', $return = 'boolean' ) {
1279  
1280      $r = bp_parse_args(
1281          $args,
1282          array(
1283              'object'        => 'user',
1284              'avatar_dir'    => 'avatars',
1285              'item_id'       => false,
1286              'original_file' => false,
1287              'crop_w'        => bp_core_avatar_full_width(),
1288              'crop_h'        => bp_core_avatar_full_height(),
1289              'crop_x'        => 0,
1290              'crop_y'        => 0,
1291          )
1292      );
1293  
1294      /**
1295       * Filters whether or not to handle cropping.
1296       *
1297       * If you want to override this function, make sure you return false.
1298       *
1299       * @since 1.2.4
1300       *
1301       * @param bool  $value Whether or not to crop.
1302       * @param array $r     Array of parsed arguments for function.
1303       */
1304      if ( ! apply_filters( 'bp_core_pre_avatar_handle_crop', true, $r ) ) {
1305          return true;
1306      }
1307  
1308      // Crop the file.
1309      $avatar_attachment = new BP_Attachment_Avatar();
1310      $cropped           = $avatar_attachment->crop( $r );
1311  
1312      // Check for errors.
1313      if ( empty( $cropped['full'] ) || empty( $cropped['thumb'] ) || is_wp_error( $cropped['full'] ) || is_wp_error( $cropped['thumb'] ) ) {
1314          return false;
1315      }
1316  
1317      if ( 'array' === $return ) {
1318          return $cropped;
1319      }
1320  
1321      return true;
1322  }
1323  
1324  /**
1325   * Ajax set an avatar for a given object and item id.
1326   *
1327   * @since 2.3.0
1328   *
1329   * @return string|null A JSON object containing success data if the crop/capture succeeded
1330   *                     error message otherwise.
1331   */
1332  function bp_avatar_ajax_set() {
1333      if ( ! bp_is_post_request() ) {
1334          wp_send_json_error();
1335      }
1336  
1337      // Check the nonce.
1338      check_admin_referer( 'bp_avatar_cropstore', 'nonce' );
1339  
1340      $avatar_data = bp_parse_args(
1341          $_POST,
1342          array(
1343              'crop_w' => bp_core_avatar_full_width(),
1344              'crop_h' => bp_core_avatar_full_height(),
1345              'crop_x' => 0,
1346              'crop_y' => 0,
1347          )
1348      );
1349  
1350      if ( empty( $avatar_data['object'] ) || empty( $avatar_data['item_id'] ) || empty( $avatar_data['original_file'] ) ) {
1351          wp_send_json_error();
1352      }
1353  
1354      // Capability check.
1355      if ( ! bp_attachments_current_user_can( 'edit_avatar', $avatar_data ) ) {
1356          wp_send_json_error();
1357      }
1358  
1359      if ( ! empty( $avatar_data['type'] ) && 'camera' === $avatar_data['type'] && 'user' === $avatar_data['object'] ) {
1360          $webcam_avatar = false;
1361  
1362          if ( ! empty( $avatar_data['original_file'] ) ) {
1363              $webcam_avatar = str_replace( array( 'data:image/png;base64,', ' ' ), array( '', '+' ), $avatar_data['original_file'] );
1364              $webcam_avatar = base64_decode( $webcam_avatar );
1365          }
1366  
1367          $cropped_webcam_avatar = bp_avatar_handle_capture( $webcam_avatar, $avatar_data['item_id'], 'array' );
1368  
1369          if ( ! $cropped_webcam_avatar ) {
1370              wp_send_json_error( array(
1371                  'feedback_code' => 1
1372              ) );
1373  
1374          } else {
1375              $return = array(
1376                  'avatar' => esc_url(
1377                      bp_core_fetch_avatar(
1378                          array(
1379                              'object'  => $avatar_data['object'],
1380                              'item_id' => $avatar_data['item_id'],
1381                              'html'    => false,
1382                              'type'    => 'full',
1383                          )
1384                      )
1385                  ),
1386                  'feedback_code' => 2,
1387                  'item_id'       => $avatar_data['item_id'],
1388              );
1389  
1390              /** This action is documented in wp-includes/deprecated.php */
1391              do_action_deprecated( 'xprofile_avatar_uploaded', array( (int) $avatar_data['item_id'], $avatar_data['type'], $avatar_data ), '6.0.0', 'bp_members_avatar_uploaded' );
1392  
1393              /**
1394               * Fires if the new avatar was successfully captured.
1395               *
1396               * @since 6.0.0
1397               * @since 10.0.0 Adds a new param: an array containing the full, thumb avatar and the timestamp.
1398               *
1399               * @param string $item_id               Inform about the user id the avatar was set for.
1400               * @param string $type                  Inform about the way the avatar was set ('camera').
1401               * @param array  $avatar_data           Array of parameters passed to the crop handler.
1402               * @param array  $cropped_webcam_avatar Array containing the full, thumb avatar and the timestamp.
1403               */
1404              do_action( 'bp_members_avatar_uploaded', (int) $avatar_data['item_id'], $avatar_data['type'], $avatar_data, $cropped_webcam_avatar );
1405  
1406              wp_send_json_success( $return );
1407          }
1408  
1409          return;
1410      }
1411  
1412      $original_file = str_replace( bp_core_avatar_url(), '', $avatar_data['original_file'] );
1413  
1414      // Set avatars dir & feedback part.
1415      if ( 'user' === $avatar_data['object'] ) {
1416          $avatar_dir = 'avatars';
1417  
1418      // Defaults to object-avatars dir.
1419      } else {
1420          $avatar_dir = sanitize_key( $avatar_data['object'] ) . '-avatars';
1421      }
1422  
1423      // Crop args.
1424      $r = array(
1425          'item_id'       => $avatar_data['item_id'],
1426          'object'        => $avatar_data['object'],
1427          'avatar_dir'    => $avatar_dir,
1428          'original_file' => $original_file,
1429          'crop_w'        => $avatar_data['crop_w'],
1430          'crop_h'        => $avatar_data['crop_h'],
1431          'crop_x'        => $avatar_data['crop_x'],
1432          'crop_y'        => $avatar_data['crop_y']
1433      );
1434  
1435      // Handle crop.
1436      $cropped_avatar = bp_core_avatar_handle_crop( $r, 'array' );
1437  
1438      if ( $cropped_avatar ) {
1439          $return = array(
1440              'avatar' => esc_url(
1441                  bp_core_fetch_avatar(
1442                      array(
1443                          'object'  => $avatar_data['object'],
1444                          'item_id' => $avatar_data['item_id'],
1445                          'html'    => false,
1446                          'type'    => 'full',
1447                      )
1448                  )
1449              ),
1450              'feedback_code' => 2,
1451              'item_id'       => $avatar_data['item_id'],
1452          );
1453  
1454          if ( 'user' === $avatar_data['object'] ) {
1455              /** This action is documented in wp-includes/deprecated.php */
1456              do_action_deprecated( 'xprofile_avatar_uploaded', array( (int) $avatar_data['item_id'], $avatar_data['type'], $r ), '6.0.0', 'bp_members_avatar_uploaded' );
1457  
1458              /** This action is documented in bp-core/bp-core-avatars.php */
1459              do_action( 'bp_members_avatar_uploaded', (int) $avatar_data['item_id'], $avatar_data['type'], $r, $cropped_avatar );
1460          } elseif ( 'group' === $avatar_data['object'] ) {
1461              /** This action is documented in bp-groups/bp-groups-screens.php */
1462              do_action( 'groups_avatar_uploaded', (int) $avatar_data['item_id'], $avatar_data['type'], $r, $cropped_avatar );
1463          }
1464  
1465          wp_send_json_success( $return );
1466      } else {
1467          wp_send_json_error( array(
1468              'feedback_code' => 1,
1469          ) );
1470      }
1471  }
1472  add_action( 'wp_ajax_bp_avatar_set', 'bp_avatar_ajax_set' );
1473  
1474  /**
1475   * Filter {@link get_avatar_url()} to use the BuddyPress user avatar URL.
1476   *
1477   * @since 2.9.0
1478   *
1479   * @param  string $retval      The URL of the avatar.
1480   * @param  mixed  $id_or_email The Gravatar to retrieve. Accepts a user_id, gravatar md5 hash,
1481   *                             user email, WP_User object, WP_Post object, or WP_Comment object.
1482   * @param  array  $args        Arguments passed to get_avatar_data(), after processing.
1483   * @return string
1484   */
1485  function bp_core_get_avatar_data_url_filter( $retval, $id_or_email, $args ) {
1486      $user = null;
1487  
1488      // Ugh, hate duplicating code; process the user identifier.
1489      if ( is_numeric( $id_or_email ) ) {
1490          $user = get_user_by( 'id', absint( $id_or_email ) );
1491      } elseif ( $id_or_email instanceof WP_User ) {
1492          // User Object
1493          $user = $id_or_email;
1494      } elseif ( $id_or_email instanceof WP_Post ) {
1495          // Post Object
1496          $user = get_user_by( 'id', (int) $id_or_email->post_author );
1497      } elseif ( $id_or_email instanceof WP_Comment ) {
1498          if ( ! empty( $id_or_email->user_id ) ) {
1499              $user = get_user_by( 'id', (int) $id_or_email->user_id );
1500          }
1501      } elseif ( is_email( $id_or_email ) ) {
1502          $user = get_user_by( 'email', $id_or_email );
1503      }
1504  
1505      // No user, so bail.
1506      if ( false === $user instanceof WP_User ) {
1507          return $retval;
1508      }
1509  
1510      // Set BuddyPress-specific avatar args.
1511      $args['item_id'] = $user->ID;
1512      $args['html']    = false;
1513  
1514      // Use the 'full' type if size is larger than BP's thumb width.
1515      if ( (int) $args['size'] > bp_core_avatar_thumb_width() ) {
1516          $args['type'] = 'full';
1517      }
1518  
1519      // Get the BuddyPress avatar URL.
1520      if ( $bp_avatar = bp_core_fetch_avatar( $args ) ) {
1521          return $bp_avatar;
1522      }
1523  
1524      return $retval;
1525  }
1526  add_filter( 'get_avatar_url', 'bp_core_get_avatar_data_url_filter', 10, 3 );
1527  
1528  /**
1529   * Is the current avatar upload error-free?
1530   *
1531   * @since 1.0.0
1532   *
1533   * @param array $file The $_FILES array.
1534   * @return bool True if no errors are found. False if there are errors.
1535   */
1536  function bp_core_check_avatar_upload( $file ) {
1537      if ( isset( $file['error'] ) && $file['error'] )
1538          return false;
1539  
1540      return true;
1541  }
1542  
1543  /**
1544   * Is the file size of the current avatar upload permitted?
1545   *
1546   * @since 1.0.0
1547   *
1548   * @param array $file The $_FILES array.
1549   * @return bool True if the avatar is under the size limit, otherwise false.
1550   */
1551  function bp_core_check_avatar_size( $file ) {
1552      if ( $file['file']['size'] > bp_core_avatar_original_max_filesize() )
1553          return false;
1554  
1555      return true;
1556  }
1557  
1558  /**
1559   * Get allowed avatar types.
1560   *
1561   * @since 2.3.0
1562   *
1563   * @return array
1564   */
1565  function bp_core_get_allowed_avatar_types() {
1566      $allowed_types = bp_attachments_get_allowed_types( 'avatar' );
1567  
1568      /**
1569       * Filters the list of allowed image types.
1570       *
1571       * @since 2.3.0
1572       *
1573       * @param array $allowed_types List of image types.
1574       */
1575      $avatar_types = (array) apply_filters( 'bp_core_get_allowed_avatar_types', $allowed_types );
1576  
1577      if ( empty( $avatar_types ) ) {
1578          $avatar_types = $allowed_types;
1579      } else {
1580          $avatar_types = array_intersect( $allowed_types, $avatar_types );
1581      }
1582  
1583      return array_values( $avatar_types );
1584  }
1585  
1586  /**
1587   * Get allowed avatar mime types.
1588   *
1589   * @since 2.3.0
1590   *
1591   * @return array
1592   */
1593  function bp_core_get_allowed_avatar_mimes() {
1594      $allowed_types  = bp_core_get_allowed_avatar_types();
1595  
1596      return bp_attachments_get_allowed_mimes( 'avatar', $allowed_types );
1597  }
1598  
1599  /**
1600   * Does the current avatar upload have an allowed file type?
1601   *
1602   * Permitted file types are JPG, GIF and PNG.
1603   *
1604   * @since 1.0.0
1605   *
1606   * @param array $file The $_FILES array.
1607   * @return bool True if the file extension is permitted, otherwise false.
1608   */
1609  function bp_core_check_avatar_type( $file ) {
1610      return bp_attachments_check_filetype( $file['file']['tmp_name'], $file['file']['name'], bp_core_get_allowed_avatar_mimes() );
1611  }
1612  
1613  /**
1614   * Fetch data from the BP root blog's upload directory.
1615   *
1616   * @since 1.8.0
1617   *
1618   * @param string $type The variable we want to return from the $bp->avatars object.
1619   *                     Only 'upload_path' and 'url' are supported. Default: 'upload_path'.
1620   * @return string The avatar upload directory path.
1621   */
1622  function bp_core_get_upload_dir( $type = 'upload_path' ) {
1623      $bp = buddypress();
1624  
1625      switch ( $type ) {
1626          case 'upload_path' :
1627              $constant = 'BP_AVATAR_UPLOAD_PATH';
1628              $key      = 'basedir';
1629  
1630              break;
1631  
1632          case 'url' :
1633              $constant = 'BP_AVATAR_URL';
1634              $key      = 'baseurl';
1635  
1636              break;
1637  
1638          default :
1639              return false;
1640  
1641              break;
1642      }
1643  
1644      // See if the value has already been calculated and stashed in the $bp global.
1645      if ( isset( $bp->avatar->$type ) ) {
1646          $retval = $bp->avatar->$type;
1647      } else {
1648          // If this value has been set in a constant, just use that.
1649          if ( defined( $constant ) ) {
1650              $retval = constant( $constant );
1651          } else {
1652  
1653              // Use cached upload dir data if available.
1654              if ( ! empty( $bp->avatar->upload_dir ) ) {
1655                  $upload_dir = $bp->avatar->upload_dir;
1656  
1657              // No cache, so query for it.
1658              } else {
1659  
1660                  // Get upload directory information from current site.
1661                  $upload_dir = bp_upload_dir();
1662  
1663                  // Stash upload directory data for later use.
1664                  $bp->avatar->upload_dir = $upload_dir;
1665              }
1666  
1667              // Directory does not exist and cannot be created.
1668              if ( ! empty( $upload_dir['error'] ) ) {
1669                  $retval = '';
1670  
1671              } else {
1672                  $retval = $upload_dir[$key];
1673  
1674                  // If $key is 'baseurl', check to see if we're on SSL
1675                  // Workaround for WP13941, WP15928, WP19037.
1676                  if ( $key == 'baseurl' && is_ssl() ) {
1677                      $retval = str_replace( 'http://', 'https://', $retval );
1678                  }
1679              }
1680  
1681          }
1682  
1683          // Stash in $bp for later use.
1684          $bp->avatar->$type = $retval;
1685      }
1686  
1687      return $retval;
1688  }
1689  
1690  /**
1691   * Get the absolute upload path for the WP installation.
1692   *
1693   * @since 1.2.0
1694   *
1695   * @return string Absolute path to WP upload directory.
1696   */
1697  function bp_core_avatar_upload_path() {
1698  
1699      /**
1700       * Filters the absolute upload path for the WP installation.
1701       *
1702       * @since 1.2.0
1703       *
1704       * @param string $value Absolute upload path for the WP installation.
1705       */
1706      return apply_filters( 'bp_core_avatar_upload_path', bp_core_get_upload_dir() );
1707  }
1708  
1709  /**
1710   * Get the raw base URL for root site upload location.
1711   *
1712   * @since 1.2.0
1713   *
1714   * @return string Full URL to current upload location.
1715   */
1716  function bp_core_avatar_url() {
1717  
1718      /**
1719       * Filters the raw base URL for root site upload location.
1720       *
1721       * @since 1.2.0
1722       *
1723       * @param string $value Raw base URL for the root site upload location.
1724       */
1725      return apply_filters( 'bp_core_avatar_url', bp_core_get_upload_dir( 'url' ) );
1726  }
1727  
1728  /**
1729   * Check if a given user ID has an uploaded avatar.
1730   *
1731   * @since 1.0.0
1732   *
1733   * @param int $user_id ID of the user whose avatar is being checked.
1734   * @return bool True if the user has uploaded a local avatar. Otherwise false.
1735   */
1736  function bp_get_user_has_avatar( $user_id = 0 ) {
1737  
1738      if ( empty( $user_id ) )
1739          $user_id = bp_displayed_user_id();
1740  
1741      $retval = false;
1742      if ( bp_core_fetch_avatar( array( 'item_id' => $user_id, 'no_grav' => true, 'html' => false, 'type' => 'full' ) ) != bp_core_avatar_default( 'local' ) )
1743          $retval = true;
1744  
1745      /**
1746       * Filters whether or not a user has an uploaded avatar.
1747       *
1748       * @since 1.6.0
1749       *
1750       * @param bool $retval  Whether or not a user has an uploaded avatar.
1751       * @param int  $user_id ID of the user being checked.
1752       */
1753      return (bool) apply_filters( 'bp_get_user_has_avatar', $retval, $user_id );
1754  }
1755  
1756  /**
1757   * Utility function for fetching an avatar dimension setting.
1758   *
1759   * @since 1.5.0
1760   *
1761   * @param string $type   Dimension type you're fetching dimensions for. 'thumb'
1762   *                       or 'full'. Default: 'thumb'.
1763   * @param string $h_or_w Which dimension is being fetched. 'height' or 'width'.
1764   *                       Default: 'height'.
1765   * @return int|bool $dim The dimension.
1766   */
1767  function bp_core_avatar_dimension( $type = 'thumb', $h_or_w = 'height' ) {
1768      $bp  = buddypress();
1769      $dim = isset( $bp->avatar->{$type}->{$h_or_w} ) ? (int) $bp->avatar->{$type}->{$h_or_w} : false;
1770  
1771      /**
1772       * Filters the avatar dimension setting.
1773       *
1774       * @since 1.5.0
1775       *
1776       * @param int|bool $dim    Dimension setting for the type.
1777       * @param string   $type   The type of avatar whose dimensions are requested. Default 'thumb'.
1778       * @param string   $h_or_w The dimension parameter being requested. Default 'height'.
1779       */
1780      return apply_filters( 'bp_core_avatar_dimension', $dim, $type, $h_or_w );
1781  }
1782  
1783  /**
1784   * Get the 'thumb' avatar width setting.
1785   *
1786   * @since 1.5.0
1787   *
1788   * @return int The 'thumb' width.
1789   */
1790  function bp_core_avatar_thumb_width() {
1791  
1792      /**
1793       * Filters the 'thumb' avatar width setting.
1794       *
1795       * @since 1.5.0
1796       *
1797       * @param int $value Value for the 'thumb' avatar width setting.
1798       */
1799      return apply_filters( 'bp_core_avatar_thumb_width', bp_core_avatar_dimension( 'thumb', 'width' ) );
1800  }
1801  
1802  /**
1803   * Get the 'thumb' avatar height setting.
1804   *
1805   * @since 1.5.0
1806   *
1807   * @return int The 'thumb' height.
1808   */
1809  function bp_core_avatar_thumb_height() {
1810  
1811      /**
1812       * Filters the 'thumb' avatar height setting.
1813       *
1814       * @since 1.5.0
1815       *
1816       * @param int $value Value for the 'thumb' avatar height setting.
1817       */
1818      return apply_filters( 'bp_core_avatar_thumb_height', bp_core_avatar_dimension( 'thumb', 'height' ) );
1819  }
1820  
1821  /**
1822   * Get the 'full' avatar width setting.
1823   *
1824   * @since 1.5.0
1825   *
1826   * @return int The 'full' width.
1827   */
1828  function bp_core_avatar_full_width() {
1829  
1830      /**
1831       * Filters the 'full' avatar width setting.
1832       *
1833       * @since 1.5.0
1834       *
1835       * @param int $value Value for the 'full' avatar width setting.
1836       */
1837      return apply_filters( 'bp_core_avatar_full_width', bp_core_avatar_dimension( 'full', 'width' ) );
1838  }
1839  
1840  /**
1841   * Get the 'full' avatar height setting.
1842   *
1843   * @since 1.5.0
1844   *
1845   * @return int The 'full' height.
1846   */
1847  function bp_core_avatar_full_height() {
1848  
1849      /**
1850       * Filters the 'full' avatar height setting.
1851       *
1852       * @since 1.5.0
1853       *
1854       * @param int $value Value for the 'full' avatar height setting.
1855       */
1856      return apply_filters( 'bp_core_avatar_full_height', bp_core_avatar_dimension( 'full', 'height' ) );
1857  }
1858  
1859  /**
1860   * Get the max width for original avatar uploads.
1861   *
1862   * @since 1.5.0
1863   *
1864   * @return int The max width for original avatar uploads.
1865   */
1866  function bp_core_avatar_original_max_width() {
1867  
1868      /**
1869       * Filters the max width for original avatar uploads.
1870       *
1871       * @since 1.5.0
1872       *
1873       * @param int $value Value for the max width.
1874       */
1875      return apply_filters( 'bp_core_avatar_original_max_width', (int) buddypress()->avatar->original_max_width );
1876  }
1877  
1878  /**
1879   * Get the max filesize for original avatar uploads.
1880   *
1881   * @since 1.5.0
1882   *
1883   * @return int The max filesize for original avatar uploads.
1884   */
1885  function bp_core_avatar_original_max_filesize() {
1886  
1887      /**
1888       * Filters the max filesize for original avatar uploads.
1889       *
1890       * @since 1.5.0
1891       *
1892       * @param int $value Value for the max filesize.
1893       */
1894      return apply_filters( 'bp_core_avatar_original_max_filesize', (int) buddypress()->avatar->original_max_filesize );
1895  }
1896  
1897  /**
1898   * Get the URL of the 'full' default avatar.
1899   *
1900   * @since 1.5.0
1901   * @since 2.6.0 Introduced `$params` and `$object_type` parameters.
1902   *
1903   * @param string $type   'local' if the fallback should be the locally-hosted version
1904   *                       of the mystery person, 'gravatar' if the fallback should be
1905   *                       Gravatar's version. Default: 'gravatar'.
1906   * @param array  $params Parameters passed to bp_core_fetch_avatar().
1907   * @return string The URL of the default avatar.
1908   */
1909  function bp_core_avatar_default( $type = 'gravatar', $params = array() ) {
1910      // Local override.
1911      if ( defined( 'BP_AVATAR_DEFAULT' ) ) {
1912          $avatar = BP_AVATAR_DEFAULT;
1913  
1914      // Use the local default image.
1915      } elseif ( 'local' === $type ) {
1916          $size = '';
1917          if (
1918              ( isset( $params['type'] ) && 'thumb' === $params['type'] && bp_core_avatar_thumb_width() <= 50 ) ||
1919              ( isset( $params['width'] ) && $params['width'] <= 50 )
1920          ) {
1921  
1922              $size = '-50';
1923          }
1924  
1925          $avatar = buddypress()->plugin_url . "bp-core/images/mystery-man{$size}.jpg";
1926  
1927      // Use Gravatar's mystery person as fallback.
1928      } else {
1929          $size = '';
1930          if ( isset( $params['type'] ) && 'thumb' === $params['type'] ) {
1931              $size = bp_core_avatar_thumb_width();
1932          } else {
1933              $size = bp_core_avatar_full_width();
1934          }
1935          $avatar = '//www.gravatar.com/avatar/00000000000000000000000000000000?d=mm&amp;s=' . $size;
1936      }
1937  
1938      /** This filter is documented in wp-includes/deprecated.php */
1939      $a = apply_filters_deprecated(
1940          'bp_core_avatar_default',
1941          array( $avatar, $params ),
1942          '8.0.0',
1943          'bp_core_avatar_gravatar_default||bp_core_default_avatar',
1944          __( 'This filter was used for 2 different purposes. If your goal was to filter the default *Gravatar*, please use `bp_core_avatar_gravatar_default` instead. Otherwise, please use `bp_core_default_avatar` instead.', 'buddypress' )
1945      );
1946  
1947      if ( ! bp_core_is_default_gravatar( $a ) && false !== strpos( $avatar, '//' ) ) {
1948          $avatar = $a;
1949      }
1950  
1951      /**
1952       * Filters the URL of the 'full' default avatar.
1953       *
1954       * @since 1.5.0
1955       * @since 2.6.0 Added `$params`.
1956       * @since 8.0.0 The name of the filter was changed to `bp_core_default_avatar`.
1957       *
1958       * @param string $avatar URL of the default avatar.
1959       * @param array  $params Params provided to bp_core_fetch_avatar().
1960       */
1961      return apply_filters( 'bp_core_default_avatar', $avatar, $params );
1962  }
1963  
1964  /**
1965   * Get the URL of the 'thumb' default avatar.
1966   *
1967   * Uses Gravatar's mystery-person avatar, unless BP_AVATAR_DEFAULT_THUMB has been
1968   * defined.
1969   *
1970   * @since 1.5.0
1971   * @since 2.6.0 Introduced `$object_type` parameter.
1972   *
1973   * @param string $type   'local' if the fallback should be the locally-hosted version
1974   *                       of the mystery person, 'gravatar' if the fallback should be
1975   *                       Gravatar's version. Default: 'gravatar'.
1976   * @param array  $params Parameters passed to bp_core_fetch_avatar().
1977   * @return string The URL of the default avatar thumb.
1978   */
1979  function bp_core_avatar_default_thumb( $type = 'gravatar', $params = array() ) {
1980      // Local override.
1981      if ( defined( 'BP_AVATAR_DEFAULT_THUMB' ) ) {
1982          $avatar = BP_AVATAR_DEFAULT_THUMB;
1983  
1984      // Use the local default image.
1985      } elseif ( 'local' === $type ) {
1986          $avatar = buddypress()->plugin_url . 'bp-core/images/mystery-man-50.jpg';
1987  
1988      // Use Gravatar's mystery person as fallback.
1989      } else {
1990          $avatar = '//www.gravatar.com/avatar/00000000000000000000000000000000?d=mm&amp;s=' . bp_core_avatar_thumb_width();
1991      }
1992  
1993      /**
1994       * Filters the URL of the 'thumb' default avatar.
1995       *
1996       * @since 1.5.0
1997       * @since 2.6.0 Added `$params`.
1998       *
1999       * @param string $avatar URL of the default avatar.
2000       * @param string $params Params provided to bp_core_fetch_avatar().
2001       */
2002      return apply_filters( 'bp_core_avatar_thumb', $avatar, $params );
2003  }
2004  
2005  /**
2006   * Reset the week parameter of the WordPress main query if needed.
2007   *
2008   * When cropping an avatar, a $_POST['w'] var is sent, setting the 'week'
2009   * parameter of the WordPress main query to this posted var. To avoid
2010   * notices, we need to make sure this 'week' query var is reset to 0.
2011   *
2012   * @since 2.2.0
2013   *
2014   * @param WP_Query|null $posts_query The main query object.
2015   */
2016  function bp_core_avatar_reset_query( $posts_query = null ) {
2017      $reset_w = false;
2018  
2019      // Group's avatar edit screen.
2020      if ( bp_is_group_admin_page() ) {
2021          $reset_w = bp_is_group_admin_screen( 'group-avatar' );
2022  
2023      // Group's avatar create screen.
2024      } elseif ( bp_is_group_create() ) {
2025          /**
2026           * We can't use bp_get_groups_current_create_step().
2027           * as it's not set yet
2028           */
2029          $reset_w = 'group-avatar' === bp_action_variable( 1 );
2030  
2031      // User's change avatar screen.
2032      } else {
2033          $reset_w = bp_is_user_change_avatar();
2034      }
2035  
2036      // A user or a group is cropping an avatar.
2037      if ( true === $reset_w && isset( $_POST['avatar-crop-submit'] ) ) {
2038          $posts_query->set( 'w', 0 );
2039      }
2040  }
2041  add_action( 'bp_parse_query', 'bp_core_avatar_reset_query', 10, 1 );
2042  
2043  /**
2044   * Checks whether Avatar UI should be loaded.
2045   *
2046   * @since 2.3.0
2047   *
2048   * @return bool True if Avatar UI should load, false otherwise.
2049   */
2050  function bp_avatar_is_front_edit() {
2051      $retval = false;
2052  
2053      if ( bp_is_user_change_avatar() && 'crop-image' !== bp_get_avatar_admin_step() ) {
2054          $retval = ! bp_core_get_root_option( 'bp-disable-avatar-uploads' );
2055      }
2056  
2057      if ( bp_is_active( 'groups' ) ) {
2058          // Group creation.
2059          if ( bp_is_group_create() && bp_is_group_creation_step( 'group-avatar' ) && 'crop-image' !== bp_get_avatar_admin_step() ) {
2060              $retval = ! bp_disable_group_avatar_uploads();
2061  
2062          // Group Manage.
2063          } elseif ( bp_is_group_admin_page() && bp_is_group_admin_screen( 'group-avatar' ) && 'crop-image' !== bp_get_avatar_admin_step() ) {
2064              $retval = ! bp_disable_group_avatar_uploads();
2065          }
2066      }
2067  
2068      /**
2069       * Use this filter if you need to :
2070       * - Load the avatar UI for a component that is !groups or !user (return true regarding your conditions)
2071       * - Completely disable the avatar UI introduced in 2.3 (eg: __return_false())
2072       *
2073       * @since 2.3.0
2074       *
2075       * @param bool $retval Whether or not to load the Avatar UI.
2076       */
2077      return apply_filters( 'bp_avatar_is_front_edit', $retval );
2078  }
2079  
2080  /**
2081   * Checks whether the Webcam Avatar UI part should be loaded.
2082   *
2083   * @since 2.3.0
2084   *
2085   * @global $is_safari
2086   * @global $is_IE
2087   *
2088   * @return bool True to load the Webcam Avatar UI part. False otherwise.
2089   */
2090  function bp_avatar_use_webcam() {
2091      global $is_safari, $is_IE, $is_chrome;
2092  
2093      /**
2094       * Do not use the webcam feature for mobile devices
2095       * to avoid possible confusions.
2096       */
2097      if ( wp_is_mobile() ) {
2098          return false;
2099      }
2100  
2101      /**
2102       * Bail when the browser does not support getUserMedia.
2103       *
2104       * @see http://caniuse.com/#feat=stream
2105       */
2106      if ( $is_safari || $is_IE || ( $is_chrome && ! is_ssl() ) ) {
2107          return false;
2108      }
2109  
2110      /**
2111       * Use this filter if you need to disable the webcam capture feature
2112       * by returning false.
2113       *
2114       * @since 2.3.0
2115       *
2116       * @param bool $value Whether or not to load Webcam Avatar UI part.
2117       */
2118      return apply_filters( 'bp_avatar_use_webcam', true );
2119  }
2120  
2121  /**
2122   * Template function to load the Avatar UI javascript templates.
2123   *
2124   * @since 2.3.0
2125   */
2126  function bp_avatar_get_templates() {
2127      if ( ! bp_avatar_is_front_edit() ) {
2128          return;
2129      }
2130  
2131      bp_attachments_get_template_part( 'avatars/index' );
2132  }
2133  
2134  /**
2135   * Trick to check if the theme's BuddyPress templates are up to date.
2136   *
2137   * If the "avatar templates" are not including the new template tag, this will
2138   * help users to get the avatar UI.
2139   *
2140   * @since 2.3.0
2141   */
2142  function bp_avatar_template_check() {
2143      if ( ! bp_avatar_is_front_edit() ) {
2144          return;
2145      }
2146  
2147      if ( ! did_action( 'bp_attachments_avatar_check_template' ) ) {
2148          bp_attachments_get_template_part( 'avatars/index' );
2149      }
2150  }
2151  
2152  /**
2153   * Informs about whether avatar history is disabled or not.
2154   *
2155   * @since 10.0.0
2156   *
2157   * @return bool True if avatar history is disabled. False otherwise.
2158   *              Default: `false`.
2159   */
2160  function bp_avatar_history_is_disabled() {
2161      /**
2162       * Filter here returning `true` to disable avatar history.
2163       *
2164       * @since 10.0.0
2165       *
2166       * @param bool $value True to disable avatar history. False otherwise.
2167       *                    Default: `false`.
2168       */
2169      return apply_filters( 'bp_disable_avatar_history', false );
2170  }
2171  
2172  /**
2173   * Get a specific version of an avatar from its history.
2174   *
2175   * @since 10.0.0
2176   *
2177   * @param int        $item_id   The item ID we need the avatar version for.
2178   * @param string     $object    The object the item ID relates to.
2179   * @param int|string $timestamp An integer Unix timestamp or a date string of the format 'Y-m-d h:i:s'.
2180   * @param string     $type      The type of avatar we need. Possible values are `thumb` and `full`.
2181   * @return array                A list of matching results, an empty array if no avatars were found.
2182   */
2183  function bp_avatar_get_version( $item_id = 0, $object = 'user', $timestamp = '', $type = 'full' ) {
2184      if ( ! $item_id || ! $timestamp ) {
2185          return array();
2186      }
2187  
2188      if ( ! is_numeric( $timestamp ) ) {
2189          $timestamp = strtotime( $timestamp );
2190      }
2191  
2192      $avatar_id = $timestamp . '-bpfull';
2193      if ( 'full' !== $type ) {
2194          $avatar_id = $timestamp . '-bpthumb';
2195      }
2196  
2197      $avatar_dir = 'avatars';
2198      if ( 'user' !== $object ) {
2199          $avatar_dir = sanitize_key( $object ) . '-avatars';
2200      }
2201  
2202      // The object avatar directory we are looking into to get the avatar url.
2203      $object_avatar_dir = trailingslashit( bp_core_avatar_upload_path() ) . $avatar_dir . '/' . $item_id;
2204  
2205      return bp_attachments_list_directory_files_recursively( $object_avatar_dir, $avatar_id );
2206  }
2207  
2208  /**
2209   * Get the list of previous avatars in history
2210   *
2211   * @since 10.0.0
2212   *
2213   * @param int    $item_id The item ID we need the avatar version for.
2214   * @param string $object  The object the item ID relates to.
2215   * @param string $type    Get the `full`, `thumb` or `both` versions.
2216   * @return array          The list of previous uploaded avatars.
2217   */
2218  function bp_avatar_get_avatars_history( $item_id = 0, $object = 'user', $type = 'full' ) {
2219      if ( ! $item_id ) {
2220          return array();
2221      }
2222  
2223      $avatar_dir = 'avatars';
2224      if ( 'user' !== $object ) {
2225          $avatar_dir = sanitize_key( $object ) . '-avatars';
2226      }
2227  
2228      // The user avatar directory we are looking into to get the avatar url.
2229      $history_dir      = trailingslashit( bp_core_avatar_upload_path() ) . $avatar_dir . '/' . $item_id . '/history';
2230      $historic_avatars = bp_attachments_list_directory_files( $history_dir );
2231  
2232      if ( ! $historic_avatars ) {
2233          return array();
2234      }
2235  
2236      $avatars     = array();
2237      $history_url = trailingslashit( bp_core_avatar_url() ) .  $avatar_dir . '/' . $item_id . '/history';
2238  
2239      foreach ( $historic_avatars as $historic_avatar ) {
2240          $prefix = str_replace( array( '-bpfull', '-bpthumb' ), '', $historic_avatar->id );
2241          $gmdate = gmdate( 'Y-m-d H:i:s', $historic_avatar->last_modified );
2242          $date   = strtotime( get_date_from_gmt( $gmdate ) );
2243  
2244          $avatars[ $historic_avatar->id ] = (object) array(
2245              'id'   => $historic_avatar->id,
2246              'name' => $historic_avatar->name,
2247              'date' => sprintf(
2248                  '%1$s (%2$s)',
2249                  date_i18n( get_option( 'date_format' ), $date ),
2250                  date_i18n( get_option( 'time_format' ), $date )
2251              ),
2252              'type' => str_replace( $prefix . '-bp', '', $historic_avatar->id ),
2253              'url'  => $history_url . '/' . $historic_avatar->name,
2254          );
2255      }
2256  
2257      if ( 'both' === $type ) {
2258          return $avatars;
2259      }
2260  
2261      return wp_filter_object_list( $avatars, array( 'type' => $type ) );
2262  }
2263  
2264  /**
2265   * Recycle a previously uploaded avatar as the current avatar.
2266   *
2267   * @since 10.0.0
2268   */
2269  function bp_avatar_ajax_recycle_previous_avatar() {
2270      if ( ! bp_is_post_request() ) {
2271          wp_send_json_error();
2272      }
2273  
2274      $avatar_data = bp_parse_args(
2275          $_POST,
2276          array(
2277              'object'    => '',
2278              'item_id'   => 0,
2279              'avatar_id' => '',
2280          )
2281      );
2282  
2283      if ( ! $avatar_data['object'] || ! $avatar_data['item_id'] || ! $avatar_data['avatar_id'] ) {
2284          wp_send_json_error();
2285      }
2286  
2287      // Check the nonce.
2288      check_admin_referer( 'bp_avatar_recycle_previous', 'nonce' );
2289  
2290      // Capability check.
2291      if ( ! bp_attachments_current_user_can( 'edit_avatar', $avatar_data ) ) {
2292          wp_send_json_error();
2293      }
2294  
2295      // Set the Avatar Attachment Instance.
2296      $avatar_attachment = new BP_Attachment_Avatar();
2297      $object            = sanitize_key( $avatar_data['object'] );
2298  
2299      if ( 'user' === $object ) {
2300          $avatar_dir = 'avatars';
2301      } else {
2302          $avatar_dir = $object . '-avatars';
2303      }
2304  
2305      $item_id         = (int) $avatar_data['item_id'];
2306      $avatar_dir_path = $avatar_attachment->upload_path . '/' . $avatar_dir . '/' . $item_id;
2307      $current_avatars = bp_attachments_list_directory_files( $avatar_dir_path );
2308      $revision_errors = array();
2309  
2310      // This path will be needed to get the avatar revision object.
2311      $avatar_full_revision_path = '';
2312  
2313      // Add a revision of the current avatar if it's not a mystery man!
2314      if ( $current_avatars ) {
2315          foreach( $current_avatars as $current_avatar ) {
2316              if ( ! isset( $current_avatar->name, $current_avatar->id, $current_avatar->path ) ) {
2317                  continue;
2318              }
2319  
2320              $is_full  = preg_match( "/-bpfull/", $current_avatar->name );
2321              $is_thumb = preg_match( "/-bpthumb/", $current_avatar->name );
2322  
2323              if ( $is_full || $is_thumb ) {
2324                  // Add a revision of the current avatar.
2325                  $revision = $avatar_attachment->add_revision(
2326                      'avatar',
2327                      array(
2328                          'file_abspath' => $current_avatar->path,
2329                          'file_id'      => $current_avatar->id,
2330                      )
2331                  );
2332  
2333                  if ( is_wp_error( $revision ) ) {
2334                      $revision_errors[] = $revision->get_error_message();
2335                  } elseif ( $is_full ) {
2336                      $avatar_full_revision_path = $revision->path;
2337                  }
2338              }
2339          }
2340      }
2341  
2342      // No errors, let's recycle the previous avatar.
2343      if ( ! $revision_errors ) {
2344          $avatar_id   = sanitize_file_name( $avatar_data['avatar_id'] );
2345          $suffix      = '-bpfull';
2346          $history_dir = trailingslashit( bp_core_avatar_upload_path() ) . $avatar_dir . '/' . $item_id . '/history';
2347          $avatars     = bp_attachments_list_directory_files( $history_dir );
2348  
2349          if ( ! isset( $avatars[ $avatar_id ] ) ) {
2350              wp_send_json_error(
2351                  array(
2352                      'message' => __( 'The profile photo you want to recycle cannot be found.', 'buddypress' ),
2353                  )
2354              );
2355          }
2356  
2357          // Init recycle vars.
2358          $recycle_timestamp = bp_core_current_time( true, 'timestamp' );
2359          $recycle_errors    = array();
2360          $avatar_types      = array(
2361              'full'  => '',
2362              'thumb' => '',
2363          );
2364  
2365          // Use the found previous avatar.
2366          $avatar                = $avatars[ $avatar_id ];
2367          $avatar_types['full']  = $avatar->path;
2368          $avatar_types['thumb'] = str_replace( $suffix, '-bpthumb', $avatar->path );
2369          $historical_types      = $avatar_types;
2370  
2371          // It's a legacy avatar, we need to crop it again and remove the thumb file that is not using a timestamp in its name.
2372          if ( ! file_exists( $avatar_types['thumb'] ) ) {
2373              $full_avatar_path = $avatar_dir_path . '/' . str_replace( 'bpfull', 'original-file', wp_basename( $avatar->path ) );
2374  
2375              // Move the full version back to avatar dir.
2376              rename( $avatar->path, $full_avatar_path );
2377  
2378              $avatar_types = $avatar_attachment->crop(
2379                  array(
2380                      'original_file' => $full_avatar_path,
2381                      'avatar_dir'    => $avatar_dir,
2382                      'object'        => $object,
2383                      'item_id'       => $item_id,
2384                  )
2385              );
2386  
2387              // loop into the history directory to delete the legacy thumb version file.
2388              foreach ( $avatars as $avatar_object ) {
2389                  $timestamp = str_replace( array( '-bpthumb', '-bpfull' ), '', $avatar_object->id );
2390  
2391                  if ( ! is_numeric( $timestamp ) && false !== strpos( $avatar_object->id, '-bpthumb' ) ) {
2392                      @unlink( $avatar_object->path );
2393                  }
2394              }
2395          } else {
2396              foreach( $avatar_types as $type_key => $avatar_path ) {
2397                  $filename  = wp_basename( $avatar_path );
2398                  $avatar_id = pathinfo( $filename, PATHINFO_FILENAME );
2399                  $recycle_path = $avatar_dir_path . '/' . str_replace( $avatar_id, $recycle_timestamp . '-bp' . $type_key, $filename );
2400  
2401                  if ( ! rename( $avatar_path, $recycle_path ) ) {
2402                      $recycle_errors[] = __( 'An unexpected error occured while recycling the previous profile photo.', 'buddypress' );
2403                  } else {
2404                      $avatar_types[ $type_key ] = $recycle_path;
2405                  }
2406              }
2407  
2408              $avatar_types = array_merge(
2409                  $avatar_types,
2410                  array(
2411                      'timestamp' => $recycle_timestamp,
2412                  )
2413              );
2414          }
2415  
2416          // No errors, fire the hook used when an avatar is set.
2417          if ( ! $recycle_errors && $historical_types['full'] !== $avatar_types['full'] ) {
2418              $r = array(
2419                  'item_id'    => $item_id,
2420                  'object'     => $object,
2421                  'avatar_dir' => $avatar_dir,
2422              );
2423  
2424              $action_hook = 'bp_members_avatar_uploaded';
2425              if ( 'group' === $object ) {
2426                  $action_hook = 'groups_avatar_uploaded';
2427              }
2428  
2429              /** This action is documented in bp-core/bp-core-avatars.php */
2430              do_action( $action_hook, $item_id, 'recycle', $r, $avatar_types );
2431          } else {
2432              $recycle_error = reset( $recycle_errors );
2433  
2434              wp_send_json_error(
2435                  array(
2436                      'message' => join( "\n", $recycle_error ),
2437                  )
2438              );
2439          }
2440      } else {
2441          wp_send_json_error(
2442              array(
2443                  'message' => join( "\n", $revision_errors ),
2444              )
2445          );
2446      }
2447  
2448      $return = array(
2449          'avatar'        => esc_url(
2450              bp_core_fetch_avatar(
2451                  array(
2452                      'object'  => $object,
2453                      'item_id' => $item_id,
2454                      'html'    => false,
2455                      'type'    => 'full',
2456                  )
2457              )
2458          ),
2459          'feedback_code' => 5,
2460          'item_id'       => $item_id,
2461      );
2462  
2463      // Get the created revision object if it exists.
2464      if ( $avatar_full_revision_path ) {
2465          $history_dir     = dirname( $avatar_full_revision_path );
2466          $avatars_history = bp_attachments_list_directory_files( $history_dir );
2467          $latest_id       = pathinfo( wp_basename( $avatar_full_revision_path ), PATHINFO_FILENAME );
2468  
2469          if ( isset( $avatars_history[ $latest_id ] ) ) {
2470              $gmdate      = gmdate( 'Y-m-d H:i:s', $avatars_history[ $latest_id ]->last_modified );
2471              $date        = strtotime( get_date_from_gmt( $gmdate ) );
2472              $history_url = trailingslashit( bp_core_avatar_url() ) .  $avatar_dir . '/' . $item_id . '/history';
2473  
2474              // Prepare the avatar object for JavaScript.
2475              $avatars_history[ $latest_id ]->date = sprintf(
2476                  '%1$s (%2$s)',
2477                  date_i18n( get_option( 'date_format' ), $date ),
2478                  date_i18n( get_option( 'time_format' ), $date )
2479              );
2480              $avatars_history[ $latest_id ]->type = 'full';
2481              $avatars_history[ $latest_id ]->url  = $history_url . '/' . $avatars_history[ $latest_id ]->name;
2482  
2483              // Remove the path.
2484              unset( $avatars_history[ $latest_id ]->path );
2485  
2486              // Set the new object to add to the revision list.
2487              $return['historicalAvatar'] = $avatars_history[ $latest_id ];
2488          }
2489      }
2490  
2491      wp_send_json_success( $return );
2492  }
2493  add_action( 'wp_ajax_bp_avatar_recycle_previous', 'bp_avatar_ajax_recycle_previous_avatar' );
2494  
2495  /**
2496   * Delete a previously uploaded avatar from avatars history.
2497   *
2498   * @since 10.0.0
2499   */
2500  function bp_avatar_ajax_delete_previous_avatar() {
2501      if ( ! bp_is_post_request() ) {
2502          wp_send_json_error();
2503      }
2504  
2505      $avatar_data = bp_parse_args(
2506          $_POST,
2507          array(
2508              'object'    => '',
2509              'item_id'   => 0,
2510              'avatar_id' => '',
2511          )
2512      );
2513  
2514      if ( ! $avatar_data['object'] || ! $avatar_data['item_id'] || ! $avatar_data['avatar_id'] ) {
2515          wp_send_json_error();
2516      }
2517  
2518      // Check the nonce.
2519      check_admin_referer( 'bp_avatar_delete_previous', 'nonce' );
2520  
2521      // Capability check.
2522      if ( ! bp_attachments_current_user_can( 'edit_avatar', $avatar_data ) ) {
2523          wp_send_json_error();
2524      }
2525  
2526      $object = sanitize_key( $avatar_data['object'] );
2527      if ( 'user' === $object ) {
2528          $avatar_dir = 'avatars';
2529      } else {
2530          $avatar_dir = $object . '-avatars';
2531      }
2532  
2533      $item_id     = (int) $avatar_data['item_id'];
2534      $avatar_id   = sanitize_file_name( $avatar_data['avatar_id'] );
2535      $suffix      = '-bpfull';
2536      $history_dir = trailingslashit( bp_core_avatar_upload_path() ) . $avatar_dir . '/' . $item_id . '/history';
2537      $avatars     = bp_attachments_list_directory_files( $history_dir );
2538  
2539      if ( ! isset( $avatars[ $avatar_id ] ) ) {
2540          wp_send_json_error(
2541              array(
2542                  'message' => __( 'The profile photo you want to delete cannot be found.', 'buddypress' ),
2543              )
2544          );
2545      }
2546  
2547      $avatar_types = array(
2548          'full'  => '',
2549          'thumb' => '',
2550      );
2551  
2552      // Use the found previous avatar.
2553      $avatar                = $avatars[ $avatar_id ];
2554      $avatar_types['full']  = $avatar->path;
2555      $avatar_types['thumb'] = str_replace( $suffix, '-bpthumb', $avatar->path );
2556  
2557      // It's a legacy avatar, we need to find the thumb version using file last modified date.
2558      if ( ! file_exists( $avatar_types['thumb'] ) ) {
2559          $avatar_types['thumb']  = '';
2560          $possible_thumb_avatars = wp_list_pluck( $avatars, 'last_modified', 'id' );
2561  
2562          foreach ( $possible_thumb_avatars as $type_id => $modified_date ) {
2563              $timediff = $avatar->last_modified - $modified_date;
2564              if ( 1000 >= absint( $timediff ) && $avatar_id !== $type_id ) {
2565                  $avatar_types['thumb'] = $avatars[ $type_id ]->path;
2566                  break;
2567              }
2568          }
2569      }
2570  
2571      // Remove the files.
2572      foreach ( $avatar_types as $avatar_path ) {
2573          if ( ! $avatar_path ) {
2574              continue;
2575          }
2576  
2577          @unlink( $avatar_path );
2578      }
2579  
2580      $timestamp = str_replace( '-bpfull', '', $avatar_id );
2581      if ( ! is_numeric( $timestamp ) ) {
2582          $timestamp = $avatar->last_modified;
2583      }
2584  
2585      /**
2586       * Hook here to run custom code once the previous avatar has been deleted.
2587       *
2588       * @since 10.0.0
2589       *
2590       * @param int $item_id   The object ID.
2591       * @param int $timestamp The avatar timestamp.
2592       */
2593      do_action( "bp_previous_{$object}_avatar_deleted", $item_id, $timestamp );
2594  
2595      // Finally inform about the deletion success.
2596      wp_send_json_success(
2597          array(
2598              'feedback_code' => 6,
2599          )
2600      );
2601  }
2602  add_action( 'wp_ajax_bp_avatar_delete_previous', 'bp_avatar_ajax_delete_previous_avatar' );


Generated: Thu Dec 5 01:00:58 2024 Cross-referenced by PHPXref 0.7.1