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