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


Generated: Mon Jun 21 01:01:38 2021 Cross-referenced by PHPXref 0.7.1