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


Generated: Mon Sep 27 01:00:56 2021 Cross-referenced by PHPXref 0.7.1