[ Index ]

PHP Cross Reference of BuddyPress

title

Body

[close]

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

   1  <?php
   2  /**
   3   * BuddyPress Attachments functions.
   4   *
   5   * @package BuddyPress
   6   * @subpackage Attachments
   7   * @since 2.3.0
   8   */
   9  
  10  // Exit if accessed directly.
  11  defined( 'ABSPATH' ) || exit;
  12  
  13  /**
  14   * Get the Attachments Uploads dir data.
  15   *
  16   * @since 2.4.0
  17   *
  18   * @param string $data The data to get. Possible values are: 'dir', 'basedir' & 'baseurl'.
  19   *                     Leave empty to get all datas.
  20   * @return string|array The needed Upload dir data.
  21   */
  22  function bp_attachments_uploads_dir_get( $data = '' ) {
  23      $attachments_dir = 'buddypress';
  24      $retval          = '';
  25  
  26      if ( 'dir' === $data ) {
  27          $retval = $attachments_dir;
  28      } else {
  29          $upload_data = bp_upload_dir();
  30  
  31          // Return empty string, if Uploads data are not available.
  32          if ( ! $upload_data ) {
  33              return $retval;
  34          }
  35  
  36          // Build the Upload data array for BuddyPress attachments.
  37          foreach ( $upload_data as $key => $value ) {
  38              if ( 'basedir' === $key || 'baseurl' === $key ) {
  39                  $upload_data[ $key ] = trailingslashit( $value ) . $attachments_dir;
  40  
  41                  // Fix for HTTPS.
  42                  if ( 'baseurl' === $key && is_ssl() ) {
  43                      $upload_data[ $key ] = str_replace( 'http://', 'https://', $upload_data[ $key ] );
  44                  }
  45              } else {
  46                  unset( $upload_data[ $key ] );
  47              }
  48          }
  49  
  50          // Add the dir to the array.
  51          $upload_data['dir'] = $attachments_dir;
  52  
  53          if ( empty( $data ) ) {
  54              $retval = $upload_data;
  55          } elseif ( isset( $upload_data[ $data ] ) ) {
  56              $retval = $upload_data[ $data ];
  57          }
  58      }
  59  
  60      /**
  61       * Filter here to edit the Attachments upload dir data.
  62       *
  63       * @since 2.4.0
  64       *
  65       * @param string|array $retval The needed Upload dir data or the full array of data
  66       * @param string       $data   The data requested
  67       */
  68      return apply_filters( 'bp_attachments_uploads_dir_get', $retval, $data );
  69  }
  70  
  71  /**
  72   * Gets the upload dir array for cover images.
  73   *
  74   * @since 3.0.0
  75   *
  76   * @return array See wp_upload_dir().
  77   */
  78  function bp_attachments_cover_image_upload_dir( $args = array() ) {
  79      // Default values are for members.
  80      $object_id = bp_displayed_user_id();
  81  
  82      if ( empty( $object_id ) ) {
  83          $object_id = bp_loggedin_user_id();
  84      }
  85  
  86      $object_directory = 'members';
  87  
  88      // We're in a group, edit default values.
  89      if ( bp_is_group() || bp_is_group_create() ) {
  90          $object_id        = bp_get_current_group_id();
  91          $object_directory = 'groups';
  92      }
  93  
  94      $r = bp_parse_args(
  95          $args,
  96          array(
  97              'object_id'        => $object_id,
  98              'object_directory' => $object_directory,
  99          ),
 100          'cover_image_upload_dir'
 101      );
 102  
 103      // Set the subdir.
 104      $subdir  = '/' . $r['object_directory'] . '/' . $r['object_id'] . '/cover-image';
 105  
 106      $upload_dir = bp_attachments_uploads_dir_get();
 107  
 108      /**
 109       * Filters the cover image upload directory.
 110       *
 111       * @since 2.4.0
 112       *
 113       * @param array $value      Array containing the path, URL, and other helpful settings.
 114       * @param array $upload_dir The original Uploads dir.
 115       */
 116      return apply_filters( 'bp_attachments_cover_image_upload_dir', array(
 117          'path'    => $upload_dir['basedir'] . $subdir,
 118          'url'     => set_url_scheme( $upload_dir['baseurl'] ) . $subdir,
 119          'subdir'  => $subdir,
 120          'basedir' => $upload_dir['basedir'],
 121          'baseurl' => set_url_scheme( $upload_dir['baseurl'] ),
 122          'error'   => false,
 123      ), $upload_dir );
 124  }
 125  
 126  /**
 127   * Get the max upload file size for any attachment.
 128   *
 129   * @since 2.4.0
 130   *
 131   * @param string $type A string to inform about the type of attachment
 132   *                     we wish to get the max upload file size for.
 133   * @return int Max upload file size for any attachment.
 134   */
 135  function bp_attachments_get_max_upload_file_size( $type = '' ) {
 136      $fileupload_maxk = bp_core_get_root_option( 'fileupload_maxk' );
 137  
 138      if ( '' === $fileupload_maxk ) {
 139          $fileupload_maxk = 5120000; // 5mb;
 140      } else {
 141          $fileupload_maxk = $fileupload_maxk * 1024;
 142      }
 143  
 144      /**
 145       * Filter here to edit the max upload file size.
 146       *
 147       * @since 2.4.0
 148       *
 149       * @param int    $fileupload_maxk Max upload file size for any attachment.
 150       * @param string $type            The attachment type (eg: 'avatar' or 'cover_image').
 151       */
 152      return apply_filters( 'bp_attachments_get_max_upload_file_size', $fileupload_maxk, $type );
 153  }
 154  
 155  /**
 156   * Get allowed types for any attachment.
 157   *
 158   * @since 2.4.0
 159   *
 160   * @param string $type The extension types to get.
 161   *                     Default: 'avatar'.
 162   * @return array The list of allowed extensions for attachments.
 163   */
 164  function bp_attachments_get_allowed_types( $type = 'avatar' ) {
 165      // Defaults to BuddyPress supported image extensions.
 166      $exts    = array( 'jpeg', 'gif', 'png' );
 167      $wp_exts = wp_get_ext_types();
 168  
 169      /**
 170       * It's not a BuddyPress feature, get the allowed extensions
 171       * matching the $type requested.
 172       */
 173      if ( 'avatar' !== $type && 'cover_image' !== $type ) {
 174          // Reset the default exts.
 175          $exts = array();
 176  
 177          if ( 'video' === $type ) {
 178              $exts = wp_get_video_extensions();
 179          } elseif ( 'audio' === $type ) {
 180              $exts = wp_get_audio_extensions();
 181          } elseif ( isset( $wp_exts[ $type ] ) ) {
 182              $exts = $wp_exts[ $type ];
 183          } else {
 184              $allowed_mimes = get_allowed_mime_types();
 185  
 186              /**
 187               * Search for allowed mimes matching the type.
 188               *
 189               * Eg: using 'application/vnd.oasis' as the $type
 190               * parameter will get all OpenOffice extensions supported
 191               * by WordPress and allowed for the current user.
 192               */
 193              if ( '' !== $type ) {
 194                  $allowed_mimes = preg_grep( '/' . addcslashes( $type, '/.+-' ) . '/', $allowed_mimes );
 195              }
 196  
 197              $allowed_types = array_keys( $allowed_mimes );
 198  
 199              // Loop to explode keys using '|'.
 200              foreach ( $allowed_types as $allowed_type ) {
 201                  $t = explode( '|', $allowed_type );
 202                  $exts = array_merge( $exts, (array) $t );
 203              }
 204          }
 205      }
 206  
 207      /**
 208       * Filter here to edit the allowed extensions by attachment type.
 209       *
 210       * @since 2.4.0
 211       *
 212       * @param array  $exts List of allowed extensions.
 213       * @param string $type The requested file type.
 214       */
 215      return apply_filters( 'bp_attachments_get_allowed_types', $exts, $type );
 216  }
 217  
 218  /**
 219   * Get allowed attachment mime types.
 220   *
 221   * @since 2.4.0
 222   *
 223   * @param string $type          The extension types to get (Optional).
 224   * @param array  $allowed_types List of allowed extensions.
 225   * @return array List of allowed mime types.
 226   */
 227  function bp_attachments_get_allowed_mimes( $type = '', $allowed_types = array() ) {
 228      if ( empty( $allowed_types ) ) {
 229          $allowed_types = bp_attachments_get_allowed_types( $type );
 230      }
 231  
 232      $wp_mimes      = wp_get_mime_types();
 233      $allowed_mimes = array();
 234  
 235      foreach ( $allowed_types as $allowed_type ) {
 236          $filetype = wp_check_filetype( '.' . $allowed_type, $wp_mimes );
 237  
 238          if ( false === $filetype['ext'] || false === $filetype['type'] ) {
 239              continue;
 240          }
 241  
 242          $allowed_mimes[ $filetype['ext'] ] = $filetype['type'];
 243      }
 244  
 245      /**
 246       * Include jpg type if jpeg is set
 247       */
 248      if ( isset( $allowed_mimes['jpeg'] ) && ! isset( $allowed_mimes['jpg'] ) ) {
 249          $allowed_mimes['jpg'] = $allowed_mimes['jpeg'];
 250      }
 251  
 252      return $allowed_mimes;
 253  }
 254  
 255  /**
 256   * Check the uploaded attachment type is allowed.
 257   *
 258   * @since 2.4.0
 259   *
 260   * @param string $file          Full path to the file.
 261   * @param string $filename      The name of the file (may differ from $file due to $file being
 262   *                              in a tmp directory).
 263   * @param array  $allowed_mimes The attachment allowed mimes (Required).
 264   * @return bool True if the attachment type is allowed. False otherwise
 265   */
 266  function bp_attachments_check_filetype( $file, $filename, $allowed_mimes ) {
 267      $filetype = wp_check_filetype_and_ext( $file, $filename, $allowed_mimes );
 268  
 269      if ( ! empty( $filetype['ext'] ) && ! empty( $filetype['type'] ) ) {
 270          return true;
 271      }
 272  
 273      return false;
 274  }
 275  
 276  /**
 277   * Use the absolute path to an image to set an attachment type for a given item.
 278   *
 279   * @since 2.4.0
 280   *
 281   * @param string $type The attachment type to create (avatar or cover_image). Default: avatar.
 282   * @param array  $args {
 283   *     @type int    $item_id   The ID of the object (Required). Default: 0.
 284   *     @type string $object    The object type (eg: group, user, blog) (Required). Default: 'user'.
 285   *     @type string $component The component for the object (eg: groups, members, blogs). Default: ''.
 286   *     @type string $image     The absolute path to the image (Required). Default: ''.
 287   *     @type int    $crop_w    Crop width. Default: 0.
 288   *     @type int    $crop_h    Crop height. Default: 0.
 289   *     @type int    $crop_x    The horizontal starting point of the crop. Default: 0.
 290   *     @type int    $crop_y    The vertical starting point of the crop. Default: 0.
 291   * }
 292   * @return bool True on success, false otherwise.
 293   */
 294  function bp_attachments_create_item_type( $type = 'avatar', $args = array() ) {
 295      if ( empty( $type ) || ( $type !== 'avatar' && $type !== 'cover_image' ) ) {
 296          return false;
 297      }
 298  
 299      $r = bp_parse_args(
 300          $args,
 301          array(
 302              'item_id'   => 0,
 303              'object'    => 'user',
 304              'component' => '',
 305              'image'     => '',
 306              'crop_w'    => 0,
 307              'crop_h'    => 0,
 308              'crop_x'    => 0,
 309              'crop_y'    => 0,
 310          ),
 311          'create_item_' . $type
 312      );
 313  
 314      if ( empty( $r['item_id'] ) || empty( $r['object'] ) || ! file_exists( $r['image'] ) || ! @getimagesize( $r['image'] ) ) {
 315          return false;
 316      }
 317  
 318      // Make sure the file path is safe.
 319      if ( 1 === validate_file( $r['image'] ) ) {
 320          return false;
 321      }
 322  
 323      // Set the component if not already done.
 324      if ( empty( $r['component'] ) ) {
 325          if ( 'user' === $r['object'] ) {
 326              $r['component'] = 'members';
 327          } else {
 328              $r['component'] = $r['object'] . 's';
 329          }
 330      }
 331  
 332      // Get allowed mimes for the Attachment type and check the image one is.
 333      $allowed_mimes = bp_attachments_get_allowed_mimes( $type );
 334      $is_allowed    = wp_check_filetype( $r['image'], $allowed_mimes );
 335  
 336      // It's not an image.
 337      if ( ! $is_allowed['ext'] ) {
 338          return false;
 339      }
 340  
 341      // Init the Attachment data.
 342      $attachment_data = array();
 343  
 344      if ( 'avatar' === $type ) {
 345          // Set crop width for the avatar if not given.
 346          if ( empty( $r['crop_w'] ) ) {
 347              $r['crop_w'] = bp_core_avatar_full_width();
 348          }
 349  
 350          // Set crop height for the avatar if not given.
 351          if ( empty( $r['crop_h'] ) ) {
 352              $r['crop_h'] = bp_core_avatar_full_height();
 353          }
 354  
 355          if ( is_callable( $r['component'] . '_avatar_upload_dir' ) ) {
 356              $dir_args = array( $r['item_id'] );
 357  
 358              // In case  of members, we need an extra argument.
 359              if ( 'members' === $r['component'] ) {
 360                  $dir_args = array( false, $r['item_id'] );
 361              }
 362  
 363              $attachment_data = call_user_func_array( $r['component'] . '_avatar_upload_dir', $dir_args );
 364          }
 365      } elseif ( 'cover_image' === $type ) {
 366          $attachment_data = bp_attachments_cover_image_upload_dir();
 367  
 368          // The BP Attachments Uploads Dir is not set, stop.
 369          if ( ! $attachment_data ) {
 370              return false;
 371          }
 372  
 373          // Default to members for xProfile.
 374          $object_subdir = 'members';
 375  
 376          if ( 'members' !== $r['component'] ) {
 377              $object_subdir = sanitize_key( $r['component'] );
 378          }
 379  
 380          // Set Subdir.
 381          $attachment_data['subdir'] = $object_subdir . '/' . $r['item_id'] . '/cover-image';
 382  
 383          // Set Path.
 384          $attachment_data['path'] = trailingslashit( $attachment_data['basedir'] ) . $attachment_data['subdir'];
 385      }
 386  
 387      if ( ! isset( $attachment_data['path'] ) || ! isset( $attachment_data['subdir'] ) ) {
 388          return false;
 389      }
 390  
 391      // It's not a regular upload, we may need to create some folders.
 392      if ( ! is_dir( $attachment_data['path'] ) ) {
 393          if ( ! wp_mkdir_p( $attachment_data['path'] ) ) {
 394              return false;
 395          }
 396      }
 397  
 398      // Set the image name and path.
 399      $image_file_name = wp_unique_filename( $attachment_data['path'], basename( $r['image'] ) );
 400      $image_file_path = $attachment_data['path'] . '/' . $image_file_name;
 401  
 402      // Copy the image file into the avatar dir.
 403      if ( ! copy( $r['image'], $image_file_path ) ) {
 404          return false;
 405      }
 406  
 407      // Init the response.
 408      $created = false;
 409  
 410      // It's an avatar, we need to crop it.
 411      if ( 'avatar' === $type ) {
 412          $created = bp_core_avatar_handle_crop(
 413              array(
 414                  'object'        => $r['object'],
 415                  'avatar_dir'    => trim( dirname( $attachment_data['subdir'] ), '/' ),
 416                  'item_id'       => (int) $r['item_id'],
 417                  'original_file' => trailingslashit( $attachment_data['subdir'] ) . $image_file_name,
 418                  'crop_w'        => $r['crop_w'],
 419                  'crop_h'        => $r['crop_h'],
 420                  'crop_x'        => $r['crop_x'],
 421                  'crop_y'        => $r['crop_y']
 422              )
 423          );
 424  
 425      // It's a cover image we need to fit it to feature's dimensions.
 426      } elseif ( 'cover_image' === $type ) {
 427          $cover_image = bp_attachments_cover_image_generate_file(
 428              array(
 429                  'file'            => $image_file_path,
 430                  'component'       => $r['component'],
 431                  'cover_image_dir' => $attachment_data['path']
 432              )
 433          );
 434  
 435          $created = ! empty( $cover_image['cover_file'] );
 436      }
 437  
 438      // Remove copied file if it fails.
 439      if ( ! $created ) {
 440          @unlink( $image_file_path );
 441      }
 442  
 443      // Return the response.
 444      return $created;
 445  }
 446  
 447  /**
 448   * Get the url or the path for a type of attachment.
 449   *
 450   * @since 2.4.0
 451   *
 452   * @param string $data whether to get the url or the path.
 453   * @param array  $args {
 454   *     @type string $object_dir  The object dir (eg: members/groups). Defaults to members.
 455   *     @type int    $item_id     The object id (eg: a user or a group id). Defaults to current user.
 456   *     @type string $type        The type of the attachment which is also the subdir where files are saved.
 457   *                               Defaults to 'cover-image'
 458   *     @type string $file        The name of the file.
 459   * }
 460   * @return string|bool The url or the path to the attachment, false otherwise
 461   */
 462  function bp_attachments_get_attachment( $data = 'url', $args = array() ) {
 463      // Default value.
 464      $attachment_data = false;
 465  
 466      $r = bp_parse_args(
 467          $args,
 468          array(
 469              'object_dir' => 'members',
 470              'item_id'    => bp_loggedin_user_id(),
 471              'type'       => 'cover-image',
 472              'file'       => '',
 473          ),
 474          'attachments_get_attachment_src'
 475      );
 476  
 477      /**
 478       * Filters whether or not to handle fetching a BuddyPress image attachment.
 479       *
 480       * If you want to override this function, make sure you return false.
 481       *
 482       * @since 2.5.1
 483       *
 484       * @param null|string $value If null is returned, proceed with default behaviour. Otherwise, value returned verbatim.
 485       * @param array $r {
 486       *     @type string $object_dir The object dir (eg: members/groups). Defaults to members.
 487       *     @type int    $item_id    The object id (eg: a user or a group id). Defaults to current user.
 488       *     @type string $type       The type of the attachment which is also the subdir where files are saved.
 489       *                              Defaults to 'cover-image'
 490       *     @type string $file       The name of the file.
 491       * }
 492       * @param string $data          The requested data `url` or `path`.
 493       */
 494      $pre_filter = apply_filters( 'bp_attachments_pre_get_attachment', null, $r, $data );
 495      if ( $pre_filter !== null ) {
 496          return $pre_filter;
 497      }
 498  
 499      // Get BuddyPress Attachments Uploads Dir datas.
 500      $bp_attachments_uploads_dir = bp_attachments_uploads_dir_get();
 501  
 502      // The BP Attachments Uploads Dir is not set, stop.
 503      if ( ! $bp_attachments_uploads_dir ) {
 504          return $attachment_data;
 505      }
 506  
 507      $type_subdir = $r['object_dir'] . '/' . $r['item_id'] . '/' . $r['type'];
 508      $type_dir    = trailingslashit( $bp_attachments_uploads_dir['basedir'] ) . $type_subdir;
 509  
 510      if ( 1 === validate_file( $type_dir ) || ! is_dir( $type_dir ) ) {
 511          return $attachment_data;
 512      }
 513  
 514      if ( ! empty( $r['file'] ) ) {
 515          if ( ! file_exists( trailingslashit( $type_dir ) . $r['file'] ) ) {
 516              return $attachment_data;
 517          }
 518  
 519          if ( 'url' === $data ) {
 520              $attachment_data = trailingslashit( $bp_attachments_uploads_dir['baseurl'] ) . $type_subdir . '/' . $r['file'];
 521          } else {
 522              $attachment_data = trailingslashit( $type_dir ) . $r['file'];
 523          }
 524  
 525      } else {
 526          $file = false;
 527  
 528          // Open the directory and get the first file.
 529          if ( $att_dir = opendir( $type_dir ) ) {
 530  
 531              while ( false !== ( $attachment_file = readdir( $att_dir ) ) ) {
 532                  // Look for the first file having the type in its name.
 533                  if ( false !== strpos( $attachment_file, $r['type'] ) && empty( $file ) ) {
 534                      $file = $attachment_file;
 535                      break;
 536                  }
 537              }
 538          }
 539  
 540          if ( empty( $file ) ) {
 541              return $attachment_data;
 542          }
 543  
 544          if ( 'url' === $data ) {
 545              $attachment_data = trailingslashit( $bp_attachments_uploads_dir['baseurl'] ) . $type_subdir . '/' . $file;
 546          } else {
 547              $attachment_data = trailingslashit( $type_dir ) . $file;
 548          }
 549      }
 550  
 551      return $attachment_data;
 552  }
 553  
 554  /**
 555   * Delete an attachment for the given arguments
 556   *
 557   * @since 2.4.0
 558   *
 559   * @see bp_attachments_get_attachment() For more information on accepted arguments.
 560   *
 561   * @param array $args Array of arguments for the attachment deletion.
 562   * @return bool True if the attachment was deleted, false otherwise.
 563   */
 564  function bp_attachments_delete_file( $args = array() ) {
 565      $attachment_path = bp_attachments_get_attachment( 'path', $args );
 566  
 567      /**
 568       * Filters whether or not to handle deleting an existing BuddyPress attachment.
 569       *
 570       * If you want to override this function, make sure you return false.
 571       *
 572       * @since 2.5.1
 573       *
 574       * @param bool $value Whether or not to delete the BuddyPress attachment.
 575       * @param array $args Array of arguments for the attachment deletion.
 576       */
 577      if ( ! apply_filters( 'bp_attachments_pre_delete_file', true, $args ) ) {
 578          return true;
 579      }
 580  
 581      if ( empty( $attachment_path ) ) {
 582          return false;
 583      }
 584  
 585      @unlink( $attachment_path );
 586      return true;
 587  }
 588  
 589  /**
 590   * Get the BuddyPress Plupload settings.
 591   *
 592   * @since 2.3.0
 593   *
 594   * @return array List of BuddyPress Plupload settings.
 595   */
 596  function bp_attachments_get_plupload_default_settings() {
 597  
 598      $max_upload_size = wp_max_upload_size();
 599  
 600      if ( ! $max_upload_size ) {
 601          $max_upload_size = 0;
 602      }
 603  
 604      $defaults = array(
 605          'runtimes'            => 'html5,flash,silverlight,html4',
 606          'file_data_name'      => 'file',
 607          'multipart_params'    => array(
 608              'action'          => 'bp_upload_attachment',
 609              '_wpnonce'        => wp_create_nonce( 'bp-uploader' ),
 610          ),
 611          'url'                 => admin_url( 'admin-ajax.php', 'relative' ),
 612          'flash_swf_url'       => includes_url( 'js/plupload/plupload.flash.swf' ),
 613          'silverlight_xap_url' => includes_url( 'js/plupload/plupload.silverlight.xap' ),
 614          'filters' => array(
 615              'max_file_size'   => $max_upload_size . 'b',
 616          ),
 617          'multipart'           => true,
 618          'urlstream_upload'    => true,
 619      );
 620  
 621      // WordPress is not allowing multi selection for iOs 7 device.. See #29602.
 622      if ( wp_is_mobile() && strpos( $_SERVER['HTTP_USER_AGENT'], 'OS 7_' ) !== false &&
 623          strpos( $_SERVER['HTTP_USER_AGENT'], 'like Mac OS X' ) !== false ) {
 624  
 625          $defaults['multi_selection'] = false;
 626      }
 627  
 628      $settings = array(
 629          'defaults' => $defaults,
 630          'browser'  => array(
 631              'mobile'    => wp_is_mobile(),
 632              'supported' => _device_can_upload(),
 633          ),
 634          'limitExceeded' => is_multisite() && ! is_upload_space_available(),
 635      );
 636  
 637      /**
 638       * Filter the BuddyPress Plupload default settings.
 639       *
 640       * @since 2.3.0
 641       *
 642       * @param array $settings Default Plupload parameters array.
 643       */
 644      return apply_filters( 'bp_attachments_get_plupload_default_settings', $settings );
 645  }
 646  
 647  /**
 648   * Builds localization strings for the BuddyPress Uploader scripts.
 649   *
 650   * @since 2.3.0
 651   *
 652   * @return array Plupload default localization strings.
 653   */
 654  function bp_attachments_get_plupload_l10n() {
 655      /**
 656       * Use this filter to edit localization strings.
 657       *
 658       * @since 2.3.0
 659       *
 660       * @param array $value An associative array of the localization strings.
 661       */
 662      return apply_filters(
 663          'bp_attachments_get_plupload_l10n',
 664          array(
 665              'queue_limit_exceeded'      => __( 'You have attempted to queue too many files.', 'buddypress' ),
 666  
 667              /* translators: %s: File name. */
 668              'file_exceeds_size_limit'   => __( '%s exceeds the maximum upload size for this site.', 'buddypress' ),
 669              'zero_byte_file'            => __( 'This file is empty. Please try another.', 'buddypress' ),
 670              'invalid_filetype'          => __( 'This file type is not allowed. Please try another.', 'buddypress' ),
 671              'not_an_image'              => __( 'This file is not an image. Please try another.', 'buddypress' ),
 672              'image_memory_exceeded'     => __( 'Memory exceeded. Please try another smaller file.', 'buddypress' ),
 673              'image_dimensions_exceeded' => __( 'This is larger than the maximum size. Please try another.', 'buddypress' ),
 674              'default_error'             => __( 'An error occurred. Please try again later.', 'buddypress' ),
 675              'missing_upload_url'        => __( 'There was a configuration error. Please contact the server administrator.', 'buddypress' ),
 676              'upload_limit_exceeded'     => __( 'You may only upload 1 file.', 'buddypress' ),
 677              'http_error'                => __( 'HTTP error.', 'buddypress' ),
 678              'upload_failed'             => __( 'Upload failed.', 'buddypress' ),
 679  
 680              /* translators: 1: Opening link tag, 2: Closing link tag. */
 681              'big_upload_failed'         => __( 'Please try uploading this file with the %1$sbrowser uploader%2$s.', 'buddypress' ),
 682  
 683              /* translators: %s: File name. */
 684              'big_upload_queued'         => __( '%s exceeds the maximum upload size for the multi-file uploader when used in your browser.', 'buddypress' ),
 685              'io_error'                  => __( 'IO error.', 'buddypress' ),
 686              'security_error'            => __( 'Security error.', 'buddypress' ),
 687              'file_cancelled'            => __( 'File canceled.', 'buddypress' ),
 688              'upload_stopped'            => __( 'Upload stopped.', 'buddypress' ),
 689              'dismiss'                   => __( 'Dismiss', 'buddypress' ),
 690              'crunching'                 => __( 'Crunching&hellip;', 'buddypress' ),
 691              'unique_file_warning'       => __( 'Make sure to upload a unique file', 'buddypress' ),
 692  
 693              /* translators: %s: File name. */
 694              'error_uploading'           => __( '&#8220;%s&#8221; has failed to upload.', 'buddypress' ),
 695              'has_avatar_warning'        => __( 'If you&#39;d like to delete the existing profile photo but not upload a new one, please use the delete tab.', 'buddypress' )
 696          )
 697      );
 698  }
 699  
 700  /**
 701   * Enqueues the script needed for the Uploader UI.
 702   *
 703   * @since 2.3.0
 704   *
 705   * @see BP_Attachment::script_data() && BP_Attachment_Avatar::script_data() for examples showing how
 706   * to set specific script data.
 707   *
 708   * @param string $class Name of the class extending BP_Attachment (eg: BP_Attachment_Avatar).
 709   * @return null|WP_Error
 710   */
 711  function bp_attachments_enqueue_scripts( $class = '' ) {
 712      // Enqueue me just once per page, please.
 713      if ( did_action( 'bp_attachments_enqueue_scripts' ) ) {
 714          return;
 715      }
 716  
 717      if ( ! $class || ! class_exists( $class ) ) {
 718          return new WP_Error( 'missing_parameter' );
 719      }
 720  
 721      // Get an instance of the class and get the script data.
 722      $attachment  = new $class;
 723      $script_data = $attachment->script_data();
 724  
 725      $args = bp_parse_args(
 726          $script_data,
 727          array(
 728              'action'            => '',
 729              'file_data_name'    => '',
 730              'max_file_size'     => 0,
 731              'browse_button'     => 'bp-browse-button',
 732              'container'         => 'bp-upload-ui',
 733              'drop_element'      => 'drag-drop-area',
 734              'bp_params'         => array(),
 735              'extra_css'         => array(),
 736              'extra_js'          => array(),
 737              'feedback_messages' => array(),
 738          ),
 739          'attachments_enqueue_scripts'
 740      );
 741  
 742      if ( empty( $args['action'] ) || empty( $args['file_data_name'] ) ) {
 743          return new WP_Error( 'missing_parameter' );
 744      }
 745  
 746      // Get the BuddyPress uploader strings.
 747      $strings = bp_attachments_get_plupload_l10n();
 748  
 749      // Get the BuddyPress uploader settings.
 750      $settings = bp_attachments_get_plupload_default_settings();
 751  
 752      // Set feedback messages.
 753      if ( ! empty( $args['feedback_messages'] ) ) {
 754          $strings['feedback_messages'] = $args['feedback_messages'];
 755      }
 756  
 757      // Use a temporary var to ease manipulation.
 758      $defaults = $settings['defaults'];
 759  
 760      // Set the upload action.
 761      $defaults['multipart_params']['action'] = $args['action'];
 762  
 763      // Set BuddyPress upload parameters if provided.
 764      if ( ! empty( $args['bp_params'] ) ) {
 765          $defaults['multipart_params']['bp_params'] = $args['bp_params'];
 766      }
 767  
 768      // Merge other arguments.
 769      $ui_args = array_intersect_key( $args, array(
 770          'file_data_name' => true,
 771          'browse_button'  => true,
 772          'container'      => true,
 773          'drop_element'   => true,
 774      ) );
 775  
 776      $defaults = array_merge( $defaults, $ui_args );
 777  
 778      if ( ! empty( $args['max_file_size'] ) ) {
 779          $defaults['filters']['max_file_size'] = $args['max_file_size'] . 'b';
 780      }
 781  
 782      // Specific to BuddyPress Avatars.
 783      if ( 'bp_avatar_upload' === $defaults['multipart_params']['action'] ) {
 784  
 785          // Include the cropping informations for avatars.
 786          $settings['crop'] = array(
 787              'full_h'  => bp_core_avatar_full_height(),
 788              'full_w'  => bp_core_avatar_full_width(),
 789          );
 790  
 791          // Avatar only need 1 file and 1 only!
 792          $defaults['multi_selection'] = false;
 793  
 794          // Does the object already has an avatar set?
 795          $has_avatar = $defaults['multipart_params']['bp_params']['has_avatar'];
 796  
 797          // What is the object the avatar belongs to?
 798          $object = $defaults['multipart_params']['bp_params']['object'];
 799  
 800          // Get The item id.
 801          $item_id = $defaults['multipart_params']['bp_params']['item_id'];
 802  
 803          // Init the Avatar nav.
 804          $avatar_nav = array(
 805              'upload'  => array(
 806                  'id'      => 'upload',
 807                  'caption' => __( 'Upload', 'buddypress' ),
 808                  'order'   => 0
 809              ),
 810              'delete'  => array(
 811                  'id'      => 'delete',
 812                  'caption' => __( 'Delete', 'buddypress' ),
 813                  'order'   => 100,
 814                  'hide'    => (int) ! $has_avatar
 815              ),
 816          );
 817  
 818          // Add the recycle view if avatar history is enabled.
 819          if ( 'user' === $object && ! bp_avatar_history_is_disabled() ) {
 820              // Look inside history to see if the user previously uploaded avatars.
 821              $avatars_history = bp_avatar_get_avatars_history( $item_id, $object );
 822  
 823              if ( $avatars_history ) {
 824                  ksort( $avatars_history );
 825                  $settings['history']       = array_values( $avatars_history );
 826                  $settings['historyNonces'] = array(
 827                      'recylePrevious' => wp_create_nonce( 'bp_avatar_recycle_previous' ),
 828                      'deletePrevious' => wp_create_nonce( 'bp_avatar_delete_previous' ),
 829                  );
 830  
 831                  $avatar_nav['recycle'] = array(
 832                      'id'      => 'recycle',
 833                      'caption' => __( 'Recycle', 'buddypress' ),
 834                      'order'   => 20,
 835                      'hide'    => (int) empty( $avatars_history ),
 836                  );
 837              }
 838          }
 839  
 840          // Create the Camera Nav if the WebCam capture feature is enabled.
 841          if ( bp_avatar_use_webcam() && 'user' === $object ) {
 842              $avatar_nav['camera'] = array(
 843                  'id'      => 'camera',
 844                  'caption' => __( 'Take Photo', 'buddypress' ),
 845                  'order'   => 10
 846              );
 847  
 848              // Set warning messages.
 849              $strings['camera_warnings'] = array(
 850                  'requesting'  => __( 'Please allow us to access to your camera.', 'buddypress'),
 851                  'loading'     => __( 'Please wait as we access your camera.', 'buddypress' ),
 852                  'loaded'      => __( 'Camera loaded. Click on the "Capture" button to take your photo.', 'buddypress' ),
 853                  'noaccess'    => __( 'It looks like you do not have a webcam or we were unable to get permission to use your webcam. Please upload a photo instead.', 'buddypress' ),
 854                  'errormsg'    => __( 'Your browser is not supported. Please upload a photo instead.', 'buddypress' ),
 855                  'videoerror'  => __( 'Video error. Please upload a photo instead.', 'buddypress' ),
 856                  'ready'       => __( 'Your profile photo is ready. Click on the "Save" button to use this photo.', 'buddypress' ),
 857                  'nocapture'   => __( 'No photo was captured. Click on the "Capture" button to take your photo.', 'buddypress' ),
 858              );
 859          }
 860  
 861          /**
 862           * Use this filter to add a navigation to a custom tool to set the object's avatar.
 863           *
 864           * @since 2.3.0
 865           *
 866           * @param array  $avatar_nav {
 867           *     An associative array of available nav items where each item is an array organized this way:
 868           *     $avatar_nav[ $nav_item_id ].
 869           *     @type string $nav_item_id The nav item id in lower case without special characters or space.
 870           *     @type string $caption     The name of the item nav that will be displayed in the nav.
 871           *     @type int    $order       An integer to specify the priority of the item nav, choose one.
 872           *                               between 1 and 99 to be after the uploader nav item and before the delete nav item.
 873           *     @type int    $hide        If set to 1 the item nav will be hidden
 874           *                               (only used for the delete nav item).
 875           * }
 876           * @param string $object The object the avatar belongs to (eg: user or group).
 877           */
 878          $settings['nav'] = bp_sort_by_key( apply_filters( 'bp_attachments_avatar_nav', $avatar_nav, $object ), 'order', 'num' );
 879  
 880      // Specific to BuddyPress cover images.
 881      } elseif ( 'bp_cover_image_upload' === $defaults['multipart_params']['action'] ) {
 882  
 883          // Cover images only need 1 file and 1 only!
 884          $defaults['multi_selection'] = false;
 885  
 886          // Default cover component is members.
 887          $cover_component = 'members';
 888  
 889          // Get the object we're editing the cover image of.
 890          $object = $defaults['multipart_params']['bp_params']['object'];
 891  
 892          // Set the cover component according to the object.
 893          if ( 'group' === $object ) {
 894              $cover_component = 'groups';
 895          } elseif ( 'user' !== $object ) {
 896              $cover_component = apply_filters( 'bp_attachments_cover_image_ui_component', $cover_component );
 897          }
 898          // Get cover image advised dimensions.
 899          $cover_dimensions = bp_attachments_get_cover_image_dimensions( $cover_component );
 900  
 901          // Set warning messages.
 902          $strings['cover_image_warnings'] = apply_filters( 'bp_attachments_cover_image_ui_warnings', array(
 903              'dimensions'  => sprintf(
 904                  /* translators: 1: the advised width size in pixels. 2: the advised height size in pixels. */
 905                  __( 'For better results, make sure to upload an image that is larger than %1$spx wide, and %2$spx tall.', 'buddypress' ),
 906                  (int) $cover_dimensions['width'],
 907                  (int) $cover_dimensions['height']
 908              ),
 909          ) );
 910      }
 911  
 912      // Set Plupload settings.
 913      $settings['defaults'] = $defaults;
 914  
 915      /**
 916       * Enqueue some extra styles if required
 917       *
 918       * Extra styles need to be registered.
 919       */
 920      if ( ! empty( $args['extra_css'] ) ) {
 921          foreach ( (array) $args['extra_css'] as $css ) {
 922              if ( empty( $css ) ) {
 923                  continue;
 924              }
 925  
 926              wp_enqueue_style( $css );
 927          }
 928      }
 929  
 930      wp_enqueue_script ( 'bp-plupload' );
 931      wp_localize_script( 'bp-plupload', 'BP_Uploader', array( 'strings' => $strings, 'settings' => $settings ) );
 932  
 933      /**
 934       * Enqueue some extra scripts if required
 935       *
 936       * Extra scripts need to be registered.
 937       */
 938      if ( ! empty( $args['extra_js'] ) ) {
 939          foreach ( (array) $args['extra_js'] as $js ) {
 940              if ( empty( $js ) ) {
 941                  continue;
 942              }
 943  
 944              wp_enqueue_script( $js );
 945          }
 946      }
 947  
 948      /**
 949       * Fires at the conclusion of bp_attachments_enqueue_scripts()
 950       * to avoid the scripts to be loaded more than once.
 951       *
 952       * @since 2.3.0
 953       */
 954      do_action( 'bp_attachments_enqueue_scripts' );
 955  }
 956  
 957  /**
 958   * Check the current user's capability to edit an avatar for a given object.
 959   *
 960   * @since 2.3.0
 961   *
 962   * @param string $capability The capability to check.
 963   * @param array  $args       An array containing the item_id and the object to check.
 964   * @return bool
 965   */
 966  function bp_attachments_current_user_can( $capability, $args = array() ) {
 967      $can = false;
 968  
 969      if ( 'edit_avatar' === $capability || 'edit_cover_image' === $capability ) {
 970          /**
 971           * Needed avatar arguments are set.
 972           */
 973          if ( isset( $args['item_id'] ) && isset( $args['object'] ) ) {
 974              // Group profile photo.
 975              if ( bp_is_active( 'groups' ) && 'group' === $args['object'] ) {
 976                  if ( bp_is_group_create() ) {
 977                      $can = (bool) groups_is_user_creator( bp_loggedin_user_id(), $args['item_id'] ) || bp_current_user_can( 'bp_moderate' );
 978                  } else {
 979                      $can = (bool) groups_is_user_admin( bp_loggedin_user_id(), $args['item_id'] ) || bp_current_user_can( 'bp_moderate' );
 980                  }
 981              // User profile photo.
 982              } elseif ( bp_is_active( 'members' ) && 'user' === $args['object'] ) {
 983                  $can = bp_loggedin_user_id() === (int) $args['item_id'] || bp_current_user_can( 'bp_moderate' );
 984              }
 985          /**
 986           * No avatar arguments, fallback to bp_user_can_create_groups()
 987           * or bp_is_item_admin()
 988           */
 989          } else {
 990              if ( bp_is_group_create() ) {
 991                  $can = bp_user_can_create_groups();
 992              } else {
 993                  $can = bp_is_item_admin();
 994              }
 995          }
 996      }
 997  
 998      return apply_filters( 'bp_attachments_current_user_can', $can, $capability, $args );
 999  }
1000  
1001  /**
1002   * Send a JSON response back to an Ajax upload request.
1003   *
1004   * @since 2.3.0
1005   *
1006   * @param bool  $success  True for a success, false otherwise.
1007   * @param bool  $is_html4 True if the Plupload runtime used is html4, false otherwise.
1008   * @param mixed $data     Data to encode as JSON, then print and die.
1009   */
1010  function bp_attachments_json_response( $success, $is_html4 = false, $data = null ) {
1011      $response = array( 'success' => $success );
1012  
1013      if ( isset( $data ) ) {
1014          $response['data'] = $data;
1015      }
1016  
1017      // Send regular json response.
1018      if ( ! $is_html4 ) {
1019          wp_send_json( $response );
1020  
1021      /**
1022       * Send specific json response
1023       * the html4 Plupload handler requires a text/html content-type for older IE.
1024       * See https://core.trac.wordpress.org/ticket/31037
1025       */
1026      } else {
1027          echo wp_json_encode( $response );
1028  
1029          wp_die();
1030      }
1031  }
1032  
1033  /**
1034   * Get an Attachment template part.
1035   *
1036   * @since 2.3.0
1037   *
1038   * @param string $slug Template part slug. eg 'uploader' for 'uploader.php'.
1039   * @return bool
1040   */
1041  function bp_attachments_get_template_part( $slug ) {
1042      $switched = false;
1043  
1044      /*
1045       * Use bp-legacy attachment template part for older bp-default themes or if in
1046       * admin area.
1047       */
1048      if ( ! bp_use_theme_compat_with_current_theme() || ( is_admin() && ( ! defined( 'DOING_AJAX' ) || ! DOING_AJAX ) ) ) {
1049          $current = bp_get_theme_compat_id();
1050          if ( 'legacy' !== $current ) {
1051              $switched = true;
1052              bp_setup_theme_compat( 'legacy' );
1053          }
1054      }
1055  
1056      // Load the template part.
1057      bp_get_template_part( 'assets/_attachments/' . $slug );
1058  
1059      if ( $switched ) {
1060          bp_setup_theme_compat( $current );
1061      }
1062  }
1063  
1064  /** Cover Image ***************************************************************/
1065  
1066  /**
1067   * Get the cover image settings
1068   *
1069   * @since 2.4.0
1070   *
1071   * @param string $component The component to get the settings for ("members" for user or "groups").
1072   * @return false|array The cover image settings in array, false on failure.
1073   */
1074  function bp_attachments_get_cover_image_settings( $component = 'members' ) {
1075      // Default parameters.
1076      $args = array();
1077  
1078      // First look in BP Theme Compat.
1079      $cover_image = bp_get_theme_compat_feature( 'cover_image' );
1080  
1081      if ( ! empty( $cover_image ) ) {
1082          $args = (array) $cover_image;
1083      }
1084  
1085      // Set default args.
1086      $default_args = bp_parse_args(
1087          $args,
1088          array(
1089              'components'    => array(),
1090              'width'         => 1300,
1091              'height'        => 225,
1092              'callback'      => '',
1093              'theme_handle'  => '',
1094              'default_cover' => '',
1095          )
1096      );
1097  
1098      // Handle deprecated xProfile fitler.
1099      if ( 'members' === $component ) {
1100          /** This filter is documented in wp-includes/deprecated.php */
1101          $args = apply_filters_deprecated( 'bp_before_xprofile_cover_image_settings_parse_args', array( $default_args ), '6.0.0', 'bp_before_members_cover_image_settings_parse_args' );
1102      }
1103  
1104      /**
1105       * Then let people override/set the feature using this dynamic filter
1106       *
1107       * Eg: for the user's profile cover image use:
1108       * add_filter( 'bp_before_members_cover_image_settings_parse_args', 'your_filter', 10, 1 );
1109       *
1110       * @since 2.4.0
1111       *
1112       * @param array $settings The cover image settings
1113       */
1114      $settings = bp_parse_args(
1115          $args,
1116          $default_args,
1117          $component . '_cover_image_settings'
1118      );
1119  
1120      // Handle deprecated xProfile fitler.
1121      if ( 'members' === $component ) {
1122          /** This filter is documented in wp-includes/deprecated.php */
1123          $settings = apply_filters_deprecated( 'bp_after_xprofile_cover_image_settings_parse_args', array( $settings ), '6.0.0', 'bp_after_members_cover_image_settings_parse_args' );
1124      }
1125  
1126      if ( empty( $settings['components'] ) || empty( $settings['callback'] ) || empty( $settings['theme_handle'] ) ) {
1127          return false;
1128      }
1129  
1130      // Current component is not supported.
1131      if ( ! in_array( $component, $settings['components'] ) ) {
1132          return false;
1133      }
1134  
1135      // Finally return the settings.
1136      return $settings;
1137  }
1138  
1139  /**
1140   * Get cover image Width and Height.
1141   *
1142   * @since 2.4.0
1143   *
1144   * @param string $component The BuddyPress component concerned ("members" for user or "groups").
1145   * @return array|bool An associative array containing the advised width and height for the cover image. False if settings are empty.
1146   */
1147  function bp_attachments_get_cover_image_dimensions( $component = 'members' ) {
1148      // Let's prevent notices when setting the warning strings.
1149      $default = array( 'width' => 0, 'height' => 0 );
1150  
1151      $settings = bp_attachments_get_cover_image_settings( $component );
1152  
1153      if ( empty( $settings ) ) {
1154          return false;
1155      }
1156  
1157      // Get width and height.
1158      $wh = array_intersect_key( $settings, $default );
1159  
1160      /**
1161       * Filter here to edit the cover image dimensions if needed.
1162       *
1163       * @since 2.4.0
1164       *
1165       * @param array  $wh       An associative array containing the width and height values.
1166       * @param array  $settings An associative array containing all the feature settings.
1167       * @param string $compnent The requested component.
1168       */
1169      return apply_filters( 'bp_attachments_get_cover_image_dimensions', $wh, $settings, $component );
1170  }
1171  
1172  /**
1173   * Are we on a page to edit a cover image?
1174   *
1175   * @since 2.4.0
1176   *
1177   * @return bool True if on a page to edit a cover image, false otherwise.
1178   */
1179  function bp_attachments_cover_image_is_edit() {
1180      $retval = false;
1181  
1182      $current_component = bp_current_component();
1183      if ( bp_is_user() ) {
1184          $current_component = 'members';
1185      }
1186  
1187      if ( ! bp_is_active( $current_component, 'cover_image' ) ) {
1188          return $retval;
1189      }
1190  
1191      if ( bp_is_user_change_cover_image() ) {
1192          $retval = ! bp_disable_cover_image_uploads();
1193      }
1194  
1195      if ( ( bp_is_group_admin_page() && 'group-cover-image' == bp_get_group_current_admin_tab() )
1196          || ( bp_is_group_create() && bp_is_group_creation_step( 'group-cover-image' ) ) ) {
1197          $retval = ! bp_disable_group_cover_image_uploads();
1198      }
1199  
1200      return apply_filters( 'bp_attachments_cover_image_is_edit', $retval, $current_component );
1201  }
1202  
1203  /**
1204   * Does the user has a cover image?
1205   *
1206   * @since 2.4.0
1207   *
1208   * @param int $user_id User ID to retrieve cover image for.
1209   * @return bool True if the user has a cover image, false otherwise.
1210   */
1211  function bp_attachments_get_user_has_cover_image( $user_id = 0 ) {
1212      if ( empty( $user_id ) ) {
1213          $user_id = bp_displayed_user_id();
1214      }
1215  
1216      $cover_src = bp_attachments_get_attachment( 'url', array(
1217          'item_id'   => $user_id,
1218      ) );
1219  
1220      return (bool) apply_filters( 'bp_attachments_get_user_has_cover_image', $cover_src, $user_id );
1221  }
1222  
1223  /**
1224   * Does the group has a cover image?
1225   *
1226   * @since 2.4.0
1227   * @since 6.0.0 Renamed the filter coherently.
1228   *
1229   * @param int $group_id Group ID to check cover image existence for.
1230   * @return bool True if the group has a cover image, false otherwise.
1231   */
1232  function bp_attachments_get_group_has_cover_image( $group_id = 0 ) {
1233      if ( empty( $group_id ) ) {
1234          $group_id = bp_get_current_group_id();
1235      }
1236  
1237      $cover_src = bp_attachments_get_attachment( 'url', array(
1238          'object_dir' => 'groups',
1239          'item_id'    => $group_id,
1240      ) );
1241  
1242      return (bool) apply_filters( 'bp_attachments_get_group_has_cover_image', $cover_src, $group_id );
1243  }
1244  
1245  /**
1246   * Generate the cover image file.
1247   *
1248   * @since 2.4.0
1249   *
1250   * @param array                          $args {
1251   *     @type string $file            The absolute path to the image. Required.
1252   *     @type string $component       The component for the object (eg: groups, members). Required.
1253   *     @type string $cover_image_dir The Cover image dir to write the image into. Required.
1254   * }
1255   * @param BP_Attachment_Cover_Image|null $cover_image_class The class to use to fit the cover image.
1256   * @return false|array An array containing cover image data on success, false otherwise.
1257   */
1258  function bp_attachments_cover_image_generate_file( $args = array(), $cover_image_class = null ) {
1259      // Bail if an argument is missing.
1260      if ( empty( $args['file'] ) || empty( $args['component'] ) || empty( $args['cover_image_dir'] ) ) {
1261          return false;
1262      }
1263  
1264      // Get advised dimensions for the cover image.
1265      $dimensions = bp_attachments_get_cover_image_dimensions( $args['component'] );
1266  
1267      // No dimensions or the file does not match with the cover image dir, stop!
1268      if ( false === $dimensions || $args['file'] !== $args['cover_image_dir'] . '/' . wp_basename( $args['file'] ) ) {
1269          return false;
1270      }
1271  
1272      if ( ! is_a( $cover_image_class, 'BP_Attachment_Cover_Image' ) ) {
1273          $cover_image_class = new BP_Attachment_Cover_Image();
1274      }
1275  
1276      $upload_dir = bp_attachments_cover_image_upload_dir();
1277  
1278      // Make sure the file is inside the Cover Image Upload path.
1279      if ( false === strpos( $args['file'], $upload_dir['basedir'] ) ) {
1280          return false;
1281      }
1282  
1283      // Resize the image so that it fit with the cover image dimensions.
1284      $cover_image  = $cover_image_class->fit( $args['file'], $dimensions );
1285      $is_too_small = false;
1286  
1287      // Image is too small in width and height.
1288      if ( empty( $cover_image ) ) {
1289          $cover_file = $cover_image_class->generate_filename( $args['file'] );
1290          @rename( $args['file'], $cover_file );
1291  
1292          // It's too small!
1293          $is_too_small = true;
1294      } elseif ( ! empty( $cover_image['path'] ) ) {
1295          $cover_file = $cover_image['path'];
1296  
1297          // Image is too small in width or height.
1298          if ( $cover_image['width'] < $dimensions['width'] || $cover_image['height'] < $dimensions['height'] ) {
1299              $is_too_small = true;
1300          }
1301      }
1302  
1303      // We were not able to generate the cover image file.
1304      if ( empty( $cover_file ) ) {
1305          return false;
1306      }
1307  
1308      // Do some clean up with old cover image, now a new one is set.
1309      $cover_basename = wp_basename( $cover_file );
1310  
1311      if ( $att_dir = opendir( $args['cover_image_dir'] ) ) {
1312          while ( false !== ( $attachment_file = readdir( $att_dir ) ) ) {
1313              // Skip directories and the new cover image.
1314              if ( 2 < strlen( $attachment_file ) && 0 !== strpos( $attachment_file, '.' ) && $cover_basename !== $attachment_file ) {
1315                  @unlink( $args['cover_image_dir'] . '/' . $attachment_file );
1316              }
1317          }
1318      }
1319  
1320      // Finally return needed data.
1321      return array(
1322          'cover_file'     => $cover_file,
1323          'cover_basename' => $cover_basename,
1324          'is_too_small'   => $is_too_small
1325      );
1326  }
1327  
1328  /**
1329   * Ajax Upload and set a cover image
1330   *
1331   * @since 2.4.0
1332   *
1333   * @return string|null A json object containing success data if the upload succeeded,
1334   *                     error message otherwise.
1335   */
1336  function bp_attachments_cover_image_ajax_upload() {
1337      if ( ! bp_is_post_request() ) {
1338          wp_die();
1339      }
1340  
1341      check_admin_referer( 'bp-uploader' );
1342  
1343      // Sending the json response will be different if the current Plupload runtime is html4.
1344      $is_html4 = ! empty( $_POST['html4' ] );
1345  
1346      if ( empty( $_POST['bp_params'] ) ) {
1347          bp_attachments_json_response( false, $is_html4 );
1348      }
1349  
1350      $bp_params = bp_parse_args(
1351          $_POST['bp_params'],
1352          array(
1353              'object'  => 'user',
1354              'item_id' => bp_loggedin_user_id(),
1355          ),
1356          'attachments_cover_image_ajax_upload'
1357      );
1358  
1359      $bp_params['item_id'] = (int) $bp_params['item_id'];
1360      $bp_params['object']  = sanitize_text_field( $bp_params['object'] );
1361  
1362      // We need the object to set the uploads dir filter.
1363      if ( empty( $bp_params['object'] ) ) {
1364          bp_attachments_json_response( false, $is_html4 );
1365      }
1366  
1367      // Capability check.
1368      if ( ! bp_attachments_current_user_can( 'edit_cover_image', $bp_params ) ) {
1369          bp_attachments_json_response( false, $is_html4 );
1370      }
1371  
1372      $bp          = buddypress();
1373      $needs_reset = array();
1374  
1375      // Member's cover image.
1376      if ( 'user' === $bp_params['object'] ) {
1377          $object_data = array( 'dir' => 'members', 'component' => 'members' );
1378  
1379          if ( ! bp_displayed_user_id() && ! empty( $bp_params['item_id'] ) ) {
1380              $needs_reset = array( 'key' => 'displayed_user', 'value' => $bp->displayed_user );
1381              $bp->displayed_user->id = $bp_params['item_id'];
1382          }
1383  
1384      // Group's cover image.
1385      } elseif ( 'group' === $bp_params['object'] ) {
1386          $object_data = array( 'dir' => 'groups', 'component' => 'groups' );
1387  
1388          if ( ! bp_get_current_group_id() && ! empty( $bp_params['item_id'] ) ) {
1389              $needs_reset = array( 'component' => 'groups', 'key' => 'current_group', 'value' => $bp->groups->current_group );
1390              $bp->groups->current_group = groups_get_group( $bp_params['item_id'] );
1391          }
1392  
1393      // Other object's cover image.
1394      } else {
1395          $object_data = apply_filters( 'bp_attachments_cover_image_object_dir', array(), $bp_params['object'] );
1396      }
1397  
1398      // Stop here in case of a missing parameter for the object.
1399      if ( empty( $object_data['dir'] ) || empty( $object_data['component'] ) ) {
1400          bp_attachments_json_response( false, $is_html4 );
1401      }
1402  
1403      /**
1404       * Filters whether or not to handle cover image uploading.
1405       *
1406       * If you want to override this function, make sure you return an array with the 'result' key set.
1407       *
1408       * @since 2.5.1
1409       *
1410       * @param array $value
1411       * @param array $bp_params
1412       * @param array $needs_reset Stores original value of certain globals we need to revert to later.
1413       * @param array $object_data
1414       */
1415      $pre_filter = apply_filters( 'bp_attachments_pre_cover_image_ajax_upload', array(), $bp_params, $needs_reset, $object_data );
1416      if ( isset( $pre_filter['result'] ) ) {
1417          bp_attachments_json_response( $pre_filter['result'], $is_html4, $pre_filter );
1418      }
1419  
1420      $cover_image_attachment = new BP_Attachment_Cover_Image();
1421      $uploaded = $cover_image_attachment->upload( $_FILES );
1422  
1423      // Reset objects.
1424      if ( ! empty( $needs_reset ) ) {
1425          if ( ! empty( $needs_reset['component'] ) ) {
1426              $bp->{$needs_reset['component']}->{$needs_reset['key']} = $needs_reset['value'];
1427          } else {
1428              $bp->{$needs_reset['key']} = $needs_reset['value'];
1429          }
1430      }
1431  
1432      if ( ! empty( $uploaded['error'] ) ) {
1433          // Upload error response.
1434          bp_attachments_json_response( false, $is_html4, array(
1435              'type'    => 'upload_error',
1436              'message' => sprintf(
1437                  /* translators: %s: the upload error message */
1438                  __( 'Upload Failed! Error was: %s', 'buddypress' ),
1439                  $uploaded['error']
1440              ),
1441          ) );
1442      }
1443  
1444      $error_message = __( 'There was a problem uploading the cover image.', 'buddypress' );
1445  
1446      $bp_attachments_uploads_dir = bp_attachments_cover_image_upload_dir();
1447  
1448      // The BP Attachments Uploads Dir is not set, stop.
1449      if ( ! $bp_attachments_uploads_dir ) {
1450          bp_attachments_json_response( false, $is_html4, array(
1451              'type'    => 'upload_error',
1452              'message' => $error_message,
1453          ) );
1454      }
1455  
1456      $cover_subdir = $object_data['dir'] . '/' . $bp_params['item_id'] . '/cover-image';
1457      $cover_dir    = trailingslashit( $bp_attachments_uploads_dir['basedir'] ) . $cover_subdir;
1458  
1459      if ( 1 === validate_file( $cover_dir ) || ! is_dir( $cover_dir ) ) {
1460          // Upload error response.
1461          bp_attachments_json_response( false, $is_html4, array(
1462              'type'    => 'upload_error',
1463              'message' => $error_message,
1464          ) );
1465      }
1466  
1467      /*
1468       * Generate the cover image so that it fit to feature's dimensions
1469       *
1470       * Unlike the avatar, uploading and generating the cover image is happening during
1471       * the same Ajax request, as we already instantiated the BP_Attachment_Cover_Image
1472       * class, let's use it.
1473       */
1474      $cover = bp_attachments_cover_image_generate_file( array(
1475          'file'            => $uploaded['file'],
1476          'component'       => $object_data['component'],
1477          'cover_image_dir' => $cover_dir
1478      ), $cover_image_attachment );
1479  
1480      if ( ! $cover ) {
1481          bp_attachments_json_response( false, $is_html4, array(
1482              'type'    => 'upload_error',
1483              'message' => $error_message,
1484          ) );
1485      }
1486  
1487      $cover_url = trailingslashit( $bp_attachments_uploads_dir['baseurl'] ) . $cover_subdir . '/' . $cover['cover_basename'];
1488  
1489      // 1 is success.
1490      $feedback_code = 1;
1491  
1492      // 0 is the size warning.
1493      if ( $cover['is_too_small'] ) {
1494          $feedback_code = 0;
1495      }
1496  
1497      // Set the name of the file.
1498      $name       = $_FILES['file']['name'];
1499      $name_parts = pathinfo( $name );
1500      $name       = trim( substr( $name, 0, - ( 1 + strlen( $name_parts['extension'] ) ) ) );
1501  
1502      // Set some arguments for filters.
1503      $item_id   = (int) $bp_params['item_id'];
1504      $component = $object_data['component'];
1505  
1506      /**
1507       * Fires if the new cover image was successfully uploaded.
1508       *
1509       * The dynamic portion of the hook will be members in case of a user's
1510       * cover image, groups in case of a group's cover image. For instance:
1511       * Use add_action( 'members_cover_image_uploaded' ) to run your specific
1512       * code once the user has set his cover image.
1513       *
1514       * @since 2.4.0
1515       * @since 3.0.0 Added $cover_url, $name, $feedback_code arguments.
1516       *
1517       * @param int    $item_id       Inform about the item id the cover image was set for.
1518       * @param string $name          Filename.
1519       * @param string $cover_url     URL to the image.
1520       * @param int    $feedback_code If value not 1, an error occured.
1521       */
1522      do_action(
1523          $component . '_cover_image_uploaded',
1524          $item_id,
1525          $name,
1526          $cover_url,
1527          $feedback_code
1528      );
1529  
1530      // Handle deprecated xProfile action.
1531      if ( 'members' === $component ) {
1532          /** This filter is documented in wp-includes/deprecated.php */
1533          do_action_deprecated(
1534              'xprofile_cover_image_uploaded',
1535              array(
1536                  $item_id,
1537                  $name,
1538                  $cover_url,
1539                  $feedback_code,
1540              ),
1541              '6.0.0',
1542              'members_cover_image_deleted'
1543          );
1544      }
1545  
1546      // Finally return the cover image url to the UI.
1547      bp_attachments_json_response( true, $is_html4, array(
1548          'name'          => $name,
1549          'url'           => $cover_url,
1550          'feedback_code' => $feedback_code,
1551      ) );
1552  }
1553  add_action( 'wp_ajax_bp_cover_image_upload', 'bp_attachments_cover_image_ajax_upload' );
1554  
1555  /**
1556   * Ajax delete a cover image for a given object and item id.
1557   *
1558   * @since 2.4.0
1559   *
1560   * @return string|null A json object containing success data if the cover image was deleted
1561   *                     error message otherwise.
1562   */
1563  function bp_attachments_cover_image_ajax_delete() {
1564      if ( ! bp_is_post_request() ) {
1565          wp_send_json_error();
1566      }
1567  
1568      if ( empty( $_POST['object'] ) || empty( $_POST['item_id'] ) || ( ! ctype_digit( $_POST['item_id'] ) && ! is_int( $_POST['item_id'] ) ) ) {
1569          wp_send_json_error();
1570      }
1571  
1572      $args = array(
1573          'object'  => sanitize_text_field( $_POST['object'] ),
1574          'item_id' => (int) $_POST['item_id'],
1575      );
1576  
1577      // Check permissions.
1578      check_admin_referer( 'bp_delete_cover_image', 'nonce' );
1579      if ( ! bp_attachments_current_user_can( 'edit_cover_image', $args ) ) {
1580          wp_send_json_error();
1581      }
1582  
1583      // Set object for the user's case.
1584      if ( 'user' === $args['object'] ) {
1585          $component = 'members';
1586          $dir       = 'members';
1587  
1588      // Set it for any other cases.
1589      } else {
1590          $component = $args['object'] . 's';
1591          $dir       = $component;
1592      }
1593  
1594      // Handle delete.
1595      if ( bp_attachments_delete_file( array( 'item_id' => $args['item_id'], 'object_dir' => $dir, 'type' => 'cover-image' ) ) ) {
1596          $item_id = (int) $args['item_id'];
1597  
1598          /**
1599           * Fires if the cover image was successfully deleted.
1600           *
1601           * The dynamic portion of the hook will be members in case of a user's
1602           * cover image, groups in case of a group's cover image. For instance:
1603           * Use add_action( 'members_cover_image_deleted' ) to run your specific
1604           * code once the user has deleted his cover image.
1605           *
1606           * @since 2.8.0
1607           *
1608           * @param int $item_id Inform about the item id the cover image was deleted for.
1609           */
1610          do_action( "{$component}_cover_image_deleted", $item_id );
1611  
1612          // Handle deprecated xProfile action.
1613          if ( 'members' === $component ) {
1614              /** This filter is documented in wp-includes/deprecated.php */
1615              do_action_deprecated( 'xprofile_cover_image_deleted', array( $item_id ), '6.0.0', 'members_cover_image_deleted' );
1616          }
1617  
1618          $response = array(
1619              'reset_url'     => '',
1620              'feedback_code' => 3,
1621          );
1622  
1623          // Get cover image settings in case there's a default header.
1624          $cover_params = bp_attachments_get_cover_image_settings( $component );
1625  
1626          // Check if there's a default cover.
1627          if ( ! empty( $cover_params['default_cover'] ) ) {
1628              $response['reset_url'] = $cover_params['default_cover'];
1629          }
1630  
1631          wp_send_json_success( $response );
1632  
1633      } else {
1634          wp_send_json_error( array(
1635              'feedback_code' => 2,
1636          ) );
1637      }
1638  }
1639  add_action( 'wp_ajax_bp_cover_image_delete', 'bp_attachments_cover_image_ajax_delete' );
1640  
1641  /**
1642   * Returns a file's mime type.
1643   *
1644   * @since 10.2.0
1645   *
1646   * @param string $file Absolute path of a file or directory.
1647   * @return false|string False if the mime type is not supported by WordPress.
1648   *                      The mime type of a file or 'directory' for a directory.
1649   */
1650  function bp_attachements_get_mime_type( $file = '' ) {
1651      $file_type = wp_check_filetype( $file, wp_get_mime_types() );
1652      $file_mime = $file_type['type'];
1653  
1654      if ( false === $file_mime && is_dir( $file ) ) {
1655          $file_mime = 'directory';
1656      }
1657  
1658      return $file_mime;
1659  }
1660  
1661  /**
1662   * Returns a BP Attachments file object.
1663   *
1664   * @since 10.2.0
1665   *
1666   * @param SplFileInfo $file The SplFileInfo file object.
1667   * @return null|object      Null if the file is not supported by WordPress.
1668   *                          A BP Attachments file object otherwise.
1669   */
1670  function bp_attachments_get_file_object( SplFileInfo $file ) {
1671      $path      = $file->getPathname();
1672      $mime_type = bp_attachements_get_mime_type( $path );
1673  
1674      // Mime type not supported by WordPress.
1675      if ( false === $mime_type ) {
1676          return null;
1677      }
1678  
1679      $_file = new stdClass();
1680  
1681      $_file->name               = $file->getfilename();
1682      $_file->path               = $path;
1683      $_file->size               = $file->getSize();
1684      $_file->type               = $file->getType();
1685      $_file->mime_type          = bp_attachements_get_mime_type( $_file->path );
1686      $_file->last_modified      = $file->getMTime();
1687      $_file->latest_access_date = $file->getATime();
1688      $_file->id                 = pathinfo( $_file->name, PATHINFO_FILENAME );
1689  
1690      return $_file;
1691  }
1692  
1693  /**
1694   * List the files of a directory.
1695   *
1696   * @since 10.0.0
1697   *
1698   * @param string $directory_path Absolute path of a directory.
1699   * @return array                 The file objects list of the directory.
1700   */
1701  function bp_attachments_list_directory_files( $directory_path = '' ) {
1702      if ( ! is_dir( $directory_path ) ) {
1703          return array();
1704      }
1705  
1706      $files    = array();
1707      $iterator = new FilesystemIterator( $directory_path, FilesystemIterator::SKIP_DOTS );
1708  
1709      foreach ( $iterator as $file ) {
1710          $supported_file = bp_attachments_get_file_object( $file );
1711  
1712          if ( is_null( $supported_file) ) {
1713              continue;
1714          }
1715  
1716          $files[ $supported_file->id ] = $supported_file;
1717      }
1718  
1719      return $files;
1720  }
1721  
1722  /**
1723   * List the files of a directory recursively and eventually find a file using its ID.
1724   *
1725   * @since 10.0.0
1726   *
1727   * @param string $directory_path Absolute path of a directory.
1728   * @param string $find           The file ID to find into the directory or its children.
1729   * @return array                 The file objects list of the directory and subdirectories.
1730   */
1731  function bp_attachments_list_directory_files_recursively( $directory_path = '', $find = '' ) {
1732      if ( ! is_dir( $directory_path ) ) {
1733          return array();
1734      }
1735  
1736      $files     = array();
1737      $directory = new RecursiveDirectoryIterator( $directory_path, FilesystemIterator::SKIP_DOTS );
1738      $iterator  = new RecursiveIteratorIterator( $directory, RecursiveIteratorIterator::CHILD_FIRST );
1739      $bp_upload = bp_upload_dir();
1740      $basedir   = str_replace( '\\', '/', $bp_upload['basedir'] );
1741  
1742      foreach ( $iterator as $file ) {
1743          $supported_file = bp_attachments_get_file_object( $file );
1744  
1745          if ( is_null( $supported_file) ) {
1746              continue;
1747          }
1748  
1749          $supported_file->parent_dir_path = str_replace( '\\', '/', dirname( $supported_file->path ) );
1750          $supported_file->parent_dir_url  = str_replace( $basedir, $bp_upload['baseurl'], $supported_file->parent_dir_path );
1751  
1752          // Ensure URL is https if SSL is set/forced.
1753          if ( is_ssl() ) {
1754              $supported_file->parent_dir_url = str_replace( 'http://', 'https://', $supported_file->parent_dir_url );
1755          }
1756  
1757          $file_id = $supported_file->id;
1758          if ( $supported_file->parent_dir_path !== $directory_path ) {
1759              $file_id = trailingslashit( str_replace( trailingslashit( $directory_path ), '', $supported_file->parent_dir_path ) ) . $file_id;
1760          }
1761  
1762          $files[ $file_id ] = $supported_file;
1763      }
1764  
1765      if ( $find ) {
1766          return wp_filter_object_list( $files, array( 'id' => $find ) );
1767      }
1768  
1769      return $files;
1770  }


Generated: Sat Dec 7 01:00:57 2024 Cross-referenced by PHPXref 0.7.1