r_size ) !== false ) { $avatar_url = $avatar_folder_url . '/' . $avatar_files[ $key ]; } } // Legacy avatar check. if ( ! isset( $avatar_url ) ) { foreach ( $avatar_files as $key => $value ) { if ( strpos( $value, $legacy_user_avatar_name ) !== false ) { $avatar_url = $avatar_folder_url . '/' . $avatar_files[ $key ]; } } // Legacy group avatar check. if ( ! isset( $avatar_url ) ) { foreach ( $avatar_files as $key => $value ) { if ( strpos( $value, $legacy_group_avatar_name ) !== false ) { $avatar_url = $avatar_folder_url . '/' . $avatar_files[ $key ]; } } } } } } // Close the avatar directory. closedir( $av_dir ); // If we found a locally uploaded avatar. if ( isset( $avatar_url ) ) { // Support custom scheme. $avatar_url = set_url_scheme( $avatar_url, $params['scheme'] ); // Return it wrapped in an element. if ( true === $params['html'] ) { /** * Filters an avatar URL wrapped in an element. * * @since 1.1.0 * * @param string $value Full element for an avatar. * @param array $params Array of parameters for the request. * @param string $value ID of the item requested. * @param string $value Subdirectory where the requested avatar should be found. * @param string $html_css_id ID attribute for avatar. * @param string $html_width Width attribute for avatar. * @param string $html_height Height attribute for avatar. * @param string $avatar_folder_url Avatar URL path. * @param string $avatar_folder_dir Avatar DIR path. */ return apply_filters( 'bp_core_fetch_avatar', '', $params, $params['item_id'], $params['avatar_dir'], $html_css_id, $html_width, $html_height, $avatar_folder_url, $avatar_folder_dir ); // ...or only the URL } else { /** * Filters a locally uploaded avatar URL. * * @since 1.2.5 * * @param string $avatar_url URL for a locally uploaded avatar. * @param array $params Array of parameters for the request. */ return apply_filters( 'bp_core_fetch_avatar_url', $avatar_url, $params ); } } } // By default, Gravatar is not pinged for groups. if ( null === $params['no_grav'] ) { $params['no_grav'] = 'group' === $params['object']; } /** * Filters whether or not to skip Gravatar check. * * @since 1.5.0 * * @param bool $value Whether or not to skip Gravatar. * @param array $params Array of parameters for the avatar request. */ if ( ! apply_filters( 'bp_core_fetch_avatar_no_grav', $params['no_grav'], $params ) ) { // Set gravatar type. if ( empty( $bp->grav_default->{$params['object']} ) ) { $default_grav = 'wavatar'; } elseif ( 'mystery' === $bp->grav_default->{$params['object']} ) { /** * Filters the Mystery person avatar src value. * * @since 1.2.0 * * @param string $value Avatar value. * @param string $value Width to display avatar at. */ $default_grav = apply_filters( 'bp_core_mysteryman_src', 'mm', $params['width'] ); } else { $default_grav = $bp->grav_default->{$params['object']}; } // Set gravatar object. if ( empty( $params['email'] ) ) { if ( 'user' === $params['object'] ) { $params['email'] = bp_core_get_user_email( $params['item_id'] ); } elseif ( 'group' === $params['object'] || 'blog' === $params['object'] ) { $params['email'] = $params['item_id'] . '-' . $params['object'] . '@' . bp_get_domain(); } } /** * Filters the Gravatar email to use. * * @since 1.1.0 * * @param string $value Email to use in Gravatar request. * @param string $value ID of the item being requested. * @param string $value Object type being requested. */ $params['email'] = apply_filters( 'bp_core_gravatar_email', $params['email'], $params['item_id'], $params['object'] ); /** * Filters the Gravatar URL host. * * @since 1.0.2 * * @param string $value Gravatar URL host. */ $gravatar = apply_filters( 'bp_gravatar_url', '//www.gravatar.com/avatar/' ); // Append email hash to Gravatar. $gravatar .= md5( strtolower( $params['email'] ) ); // Main Gravatar URL args. $url_args = array( 's' => $params['width'], ); // Custom Gravatar URL args. if ( ! empty( $params['force_default'] ) ) { $url_args['f'] = 'y'; $url_args['d'] = $params['default']; } if ( ! empty( $params['rating'] ) ) { $url_args['r'] = strtolower( $params['rating'] ); } /** This filter is documented in wp-includes/deprecated.php */ $d = apply_filters_deprecated( 'bp_core_avatar_default', array( $default_grav, $params ), '8.0.0', 'bp_core_avatar_gravatar_default||bp_core_default_avatar', __( 'This filter was used for 2 different purposes. If your goal was to filter the default *Gravatar*, please use `bp_core_avatar_gravatar_default` instead. Otherwise, please use `bp_core_default_avatar` instead.', 'buddypress' ) ); if ( bp_core_is_default_gravatar( $d ) ) { $default_grav = $d; } /** * Filters the Gravatar "d" parameter. * * @since 2.6.0 * @since 8.0.0 The name of the filter was changed to `bp_core_avatar_gravatar_default`. * * @param string $default_grav The avatar default. * @param array $params The avatar's data. */ $default_grav = apply_filters( 'bp_core_avatar_gravatar_default', $default_grav, $params ); // Only set default image if 'Gravatar Logo' is not requested. if ( ! $params['force_default'] && 'gravatar_default' !== $default_grav ) { $url_args['d'] = $default_grav; } // Set up the Gravatar URL. $gravatar = esc_url( add_query_arg( rawurlencode_deep( array_filter( $url_args ) ), $gravatar ) ); // No avatar was found, and we've been told not to use a gravatar. } else { /** * Filters the avatar default when Gravatar is not used. * * This is a variable filter dependent on the avatar type being requested. * * @since 1.5.0 * * @param string $value Default avatar for non-gravatar requests. * @param array $params Array of parameters for the avatar request. */ $gravatar = apply_filters( 'bp_core_default_avatar_' . $params['object'], bp_core_avatar_default( 'local', $params ), $params ); } if ( true === $params['html'] ) { /** This filter is documented in bp-core/bp-core-avatars.php */ return apply_filters( 'bp_core_fetch_avatar', '', $params, $params['item_id'], $params['avatar_dir'], $html_css_id, $html_width, $html_height, $avatar_folder_url, $avatar_folder_dir ); } else { /** This filter is documented in bp-core/bp-core-avatars.php */ return apply_filters( 'bp_core_fetch_avatar_url', $gravatar, $params ); } } /** * Delete an existing avatar. * * @since 1.1.0 * * @param array|string $args { * Array of function parameters. * @type bool|int $item_id ID of the item whose avatar you're deleting. * Defaults to the current item of type $object. * @type string $object Object type of the item whose avatar you're * deleting. 'user', 'group', 'blog', or custom. * Default: 'user'. * @type bool|string $avatar_dir Subdirectory where avatar is located. * Default: false, which falls back on the default location * corresponding to the $object. * } * @return bool */ function bp_core_delete_existing_avatar( $args = '' ) { $defaults = array( 'item_id' => false, 'object' => 'user', // User OR group OR blog OR custom type (if you use filters). 'avatar_dir' => false, ); $args = bp_parse_args( $args, $defaults ); /** * Filters whether or not to handle deleting an existing avatar. * * If you want to override this function, make sure you return false. * * @since 2.5.1 * * @param bool $value Whether or not to delete the avatar. * @param array $args { * Array of function parameters. * * @type bool|int $item_id ID of the item whose avatar you're deleting. * Defaults to the current item of type $object. * @type string $object Object type of the item whose avatar you're * deleting. 'user', 'group', 'blog', or custom. * Default: 'user'. * @type bool|string $avatar_dir Subdirectory where avatar is located. * Default: false, which falls back on the default location * corresponding to the $object. * } */ if ( ! apply_filters( 'bp_core_pre_delete_existing_avatar', true, $args ) ) { return true; } if ( empty( $args['item_id'] ) ) { if ( 'user' === $args['object'] ) { $args['item_id'] = bp_displayed_user_id(); } elseif ( 'group' === $args['object'] ) { $args['item_id'] = buddypress()->groups->current_group->id; } elseif ( 'blog' === $args['object'] ) { $args['item_id'] = get_current_blog_id(); } /** This filter is documented in bp-core/bp-core-avatars.php */ $item_id = (int) apply_filters( 'bp_core_avatar_item_id', $args['item_id'], $args['object'] ); } else { $item_id = (int) str_replace( '.', '', $args['item_id'] ); } if ( ! $item_id ) { return false; } if ( empty( $args['avatar_dir'] ) ) { if ( 'user' === $args['object'] ) { $args['avatar_dir'] = 'avatars'; } elseif ( 'group' === $args['object'] ) { $args['avatar_dir'] = 'group-avatars'; } elseif ( 'blog' === $args['object'] ) { $args['avatar_dir'] = 'blog-avatars'; } /** This filter is documented in bp-core/bp-core-avatars.php */ $avatar_dir = apply_filters( 'bp_core_avatar_dir', $args['avatar_dir'], $args['object'] ); } else { $avatar_dir = $args['avatar_dir']; } if ( ! $avatar_dir ) { return false; } /** This filter is documented in bp-core/bp-core-avatars.php */ $avatar_folder_dir = apply_filters( 'bp_core_avatar_folder_dir', bp_core_avatar_upload_path() . '/' . $avatar_dir . '/' . $item_id, $item_id, $args['object'], $avatar_dir ); if ( ! is_dir( $avatar_folder_dir ) ) { return false; } if ( $av_dir = opendir( $avatar_folder_dir ) ) { while ( false !== ( $avatar_file = readdir( $av_dir ) ) ) { if ( ( preg_match( '/-bpfull/', $avatar_file ) || preg_match( '/-bpthumb/', $avatar_file ) ) && '.' !== $avatar_file && '..' !== $avatar_file ) { @unlink( $avatar_folder_dir . '/' . $avatar_file ); } } } closedir( $av_dir ); @rmdir( $avatar_folder_dir ); /** * Fires after deleting an existing avatar. * * @since 1.1.0 * * @param array $args Array of arguments used for avatar deletion. */ do_action( 'bp_core_delete_existing_avatar', $args ); return true; } /** * Ajax delete an avatar for a given object and item id. * * @since 2.3.0 * * @return string|null A JSON object containing success data if the avatar was deleted, * error message otherwise. */ function bp_avatar_ajax_delete() { if ( ! bp_is_post_request() ) { wp_send_json_error(); } $avatar_data = $_POST; if ( empty( $avatar_data['object'] ) || empty( $avatar_data['item_id'] ) ) { wp_send_json_error(); } $nonce = 'bp_delete_avatar_link'; if ( 'group' === $avatar_data['object'] ) { $nonce = 'bp_group_avatar_delete'; } // Check the nonce. check_admin_referer( $nonce, 'nonce' ); // Capability check. if ( ! bp_attachments_current_user_can( 'edit_avatar', $avatar_data ) ) { wp_send_json_error(); } // Handle delete. if ( bp_core_delete_existing_avatar( array( 'item_id' => $avatar_data['item_id'], 'object' => $avatar_data['object'], ) ) ) { $return = array( 'avatar' => esc_url( bp_core_fetch_avatar( array( 'object' => $avatar_data['object'], 'item_id' => $avatar_data['item_id'], 'html' => false, 'type' => 'full', ) ) ), 'feedback_code' => 4, 'item_id' => $avatar_data['item_id'], ); wp_send_json_success( $return ); } else { wp_send_json_error( array( 'feedback_code' => 3, ) ); } } add_action( 'wp_ajax_bp_avatar_delete', 'bp_avatar_ajax_delete' ); /** * Handle avatar uploading. * * The functions starts off by checking that the file has been uploaded * properly using bp_core_check_avatar_upload(). It then checks that the file * size is within limits, and that it has an accepted file extension (jpg, gif, * png). If everything checks out, crop the image and move it to its real * location. * * @since 1.1.0 * * @see bp_core_check_avatar_upload() * @see bp_core_check_avatar_type() * * @param array $file The appropriate entry the from $_FILES superglobal. * @param string $upload_dir_filter A filter to be applied to 'upload_dir'. * @return bool */ function bp_core_avatar_handle_upload( $file, $upload_dir_filter ) { /** * Filters whether or not to handle uploading. * * If you want to override this function, make sure you return false. * * @since 1.2.4 * * @param bool $value Whether or not to crop. * @param array $file Appropriate entry from $_FILES superglobal. * @parma string $upload_dir_filter A filter to be applied to 'upload_dir'. */ if ( ! apply_filters( 'bp_core_pre_avatar_handle_upload', true, $file, $upload_dir_filter ) ) { return true; } // Setup some variables. $bp = buddypress(); $upload_path = bp_core_avatar_upload_path(); // Upload the file. $avatar_attachment = new BP_Attachment_Avatar(); $bp->avatar_admin->original = $avatar_attachment->upload( $file, $upload_dir_filter ); // In case of an error, stop the process and display a feedback to the user. if ( ! empty( $bp->avatar_admin->original['error'] ) ) { /* translators: %s: the upload error message */ bp_core_add_message( sprintf( __( 'Upload Failed! Error was: %s', 'buddypress' ), $bp->avatar_admin->original['error'] ), 'error' ); return false; } // The Avatar UI available width. $ui_available_width = 0; // Try to set the ui_available_width using the avatar_admin global. if ( isset( $bp->avatar_admin->ui_available_width ) ) { $ui_available_width = $bp->avatar_admin->ui_available_width; } // Maybe resize. $original_file_size = $avatar_attachment->get_image_data( $bp->avatar_admin->original['file'] ); $bp->avatar_admin->resized = $avatar_attachment->shrink( $bp->avatar_admin->original['file'], $ui_available_width ); $bp->avatar_admin->image = new stdClass(); // We only want to handle one image after resize. if ( empty( $bp->avatar_admin->resized ) || is_wp_error( $bp->avatar_admin->resized ) ) { $bp->avatar_admin->image->file = $bp->avatar_admin->original['file']; $bp->avatar_admin->image->dir = str_replace( $upload_path, '', $bp->avatar_admin->original['file'] ); } else { $bp->avatar_admin->image->file = $bp->avatar_admin->resized['path']; $bp->avatar_admin->image->dir = str_replace( $upload_path, '', $bp->avatar_admin->resized['path'] ); @unlink( $bp->avatar_admin->original['file'] ); } // Check for WP_Error on what should be an image. if ( is_wp_error( $bp->avatar_admin->image->dir ) ) { /* translators: %s: the upload error message */ bp_core_add_message( sprintf( __( 'Upload failed! Error was: %s', 'buddypress' ), $bp->avatar_admin->image->dir->get_error_message() ), 'error' ); return false; } // If the uploaded image is smaller than the "full" dimensions, throw a warning. if ( $avatar_attachment->is_too_small( $bp->avatar_admin->image->file ) ) { if ( isset( $original_file_size['width'] ) && $original_file_size['width'] > bp_core_avatar_full_width() ) { $aspect_ratio = number_format_i18n( bp_core_avatar_full_width() / bp_core_avatar_full_height(), 2 ); /* translators: %s: the value of the aspect ratio. */ bp_core_add_message( sprintf( __( 'The aspect ratio of the image you selected is too great compared to the profile photo one. For best results, upload a picture having an aspect ratio closer to %s.', 'buddypress' ), $aspect_ratio ), 'error' ); } else { /* translators: 1: the advised width size in pixels. 2: the advised height size in pixels. */ bp_core_add_message( sprintf( __( 'You have selected an image that is smaller than recommended. For best results, upload a picture larger than %1$d x %2$d pixels.', 'buddypress' ), bp_core_avatar_full_width(), bp_core_avatar_full_height() ), 'error' ); } } // Set the url value for the image. $bp->avatar_admin->image->url = bp_core_avatar_url() . $bp->avatar_admin->image->dir; return true; } /** * Ajax upload an avatar. * * @since 2.3.0 */ function bp_avatar_ajax_upload() { if ( ! bp_is_post_request() ) { wp_die(); } /** * Sending the json response will be different if * the current Plupload runtime is html4. */ $is_html4 = false; if ( ! empty( $_POST['html4'] ) ) { $is_html4 = true; } // Check the nonce. check_admin_referer( 'bp-uploader' ); // Init the BuddyPress parameters. $bp_params = array(); // We need it to carry on. if ( ! empty( $_POST['bp_params'] ) ) { $bp_params = $_POST['bp_params']; } else { bp_attachments_json_response( false, $is_html4 ); } // We need the object to set the uploads dir filter. if ( empty( $bp_params['object'] ) ) { bp_attachments_json_response( false, $is_html4 ); } // Capability check. if ( ! bp_attachments_current_user_can( 'edit_avatar', $bp_params ) ) { bp_attachments_json_response( false, $is_html4 ); } $bp = buddypress(); $bp_params['upload_dir_filter'] = ''; $needs_reset = array(); if ( 'user' === $bp_params['object'] && bp_is_active( 'members' ) ) { $bp_params['upload_dir_filter'] = 'bp_members_avatar_upload_dir'; if ( ! bp_displayed_user_id() && ! empty( $bp_params['item_id'] ) ) { $needs_reset = array( 'key' => 'displayed_user', 'value' => $bp->displayed_user, ); $bp->displayed_user->id = $bp_params['item_id']; } } elseif ( 'group' === $bp_params['object'] && bp_is_active( 'groups' ) ) { $bp_params['upload_dir_filter'] = 'groups_avatar_upload_dir'; if ( ! bp_get_current_group_id() && ! empty( $bp_params['item_id'] ) ) { $needs_reset = array( 'component' => 'groups', 'key' => 'current_group', 'value' => $bp->groups->current_group, ); $bp->groups->current_group = groups_get_group( $bp_params['item_id'] ); } } else { /** * Filter here to deal with other components. * * @since 2.3.0 * * @var array $bp_params the BuddyPress Ajax parameters. */ $bp_params = apply_filters( 'bp_core_avatar_ajax_upload_params', $bp_params ); } if ( ! isset( $bp->avatar_admin ) ) { $bp->avatar_admin = new stdClass(); } /** * The BuddyPress upload parameters is including the Avatar UI Available width, * add it to the avatar_admin global for a later use. */ if ( isset( $bp_params['ui_available_width'] ) ) { $bp->avatar_admin->ui_available_width = (int) $bp_params['ui_available_width']; } // Upload the avatar. $avatar = bp_core_avatar_handle_upload( $_FILES, $bp_params['upload_dir_filter'] ); // Reset objects. if ( ! empty( $needs_reset ) ) { if ( ! empty( $needs_reset['component'] ) ) { $bp->{$needs_reset['component']}->{$needs_reset['key']} = $needs_reset['value']; } else { $bp->{$needs_reset['key']} = $needs_reset['value']; } } // Init the feedback message. $feedback_message = false; if ( ! empty( $bp->template_message ) ) { $feedback_message = $bp->template_message; // Remove template message. $bp->template_message = false; $bp->template_message_type = false; @setcookie( 'bp-message', false, time() - 1000, COOKIEPATH, COOKIE_DOMAIN, is_ssl() ); @setcookie( 'bp-message-type', false, time() - 1000, COOKIEPATH, COOKIE_DOMAIN, is_ssl() ); } if ( empty( $avatar ) ) { // Default upload error. $message = __( 'Upload failed.', 'buddypress' ); // Use the template message if set. if ( ! empty( $feedback_message ) ) { $message = $feedback_message; } // Upload error reply. bp_attachments_json_response( false, $is_html4, array( 'type' => 'upload_error', 'message' => $message, ) ); } if ( empty( $bp->avatar_admin->image->file ) ) { bp_attachments_json_response( false, $is_html4 ); } $uploaded_image = @getimagesize( $bp->avatar_admin->image->file ); // Set the name of the file. $name = $_FILES['file']['name']; $name_parts = pathinfo( $name ); $name = trim( substr( $name, 0, - ( 1 + strlen( $name_parts['extension'] ) ) ) ); // Finally return the avatar to the editor. bp_attachments_json_response( true, $is_html4, array( 'name' => $name, 'url' => $bp->avatar_admin->image->url, 'width' => $uploaded_image[0], 'height' => $uploaded_image[1], 'feedback' => $feedback_message, ) ); } add_action( 'wp_ajax_bp_avatar_upload', 'bp_avatar_ajax_upload' ); /** * Handle avatar webcam capture. * * @since 2.3.0 * @since 10.0.0 Adds the `$return` param to eventually return the crop result. * * @param string $data Optional. Base64 encoded image. * @param int $item_id Optional. Item to associate. * @param string $retval Optional. Whether to get the crop `array` or a `boolean`. Defaults to `boolean`. * @return array|bool */ function bp_avatar_handle_capture( $data = '', $item_id = 0, $retval = 'boolean' ) { $return = $retval; $item_id = (int) $item_id; if ( empty( $data ) || empty( $item_id ) ) { return false; } /** * Filters whether or not to handle avatar webcam capture. * * If you want to override this function, make sure you return false. * * @since 2.5.1 * * @param bool $value Whether or not to crop. * @param string $data Base64 encoded image. * @param int $item_id Item to associate. */ if ( ! apply_filters( 'bp_avatar_pre_handle_capture', true, $data, $item_id ) ) { return true; } $avatar_dir = bp_core_avatar_upload_path() . '/avatars'; // It's not a regular upload, we may need to create this folder. if ( ! file_exists( $avatar_dir ) ) { if ( ! wp_mkdir_p( $avatar_dir ) ) { return false; } } /** * Filters the Avatar folder directory. * * @since 2.3.0 * * @param string $avatar_dir Directory for storing avatars. * @param int $item_id ID of the item being acted on. * @param string $value Avatar type. * @param string $value Avatars word. */ $avatar_folder_dir = apply_filters( 'bp_core_avatar_folder_dir', $avatar_dir . '/' . $item_id, $item_id, 'user', 'avatars' ); // It's not a regular upload, we may need to create this folder. if ( ! is_dir( $avatar_folder_dir ) ) { if ( ! wp_mkdir_p( $avatar_folder_dir ) ) { return false; } } $original_file = $avatar_folder_dir . '/webcam-capture-' . $item_id . '.png'; if ( file_put_contents( $original_file, $data ) ) { $avatar_to_crop = str_replace( bp_core_avatar_upload_path(), '', $original_file ); // Crop to default values. $crop_args = array( 'item_id' => $item_id, 'original_file' => $avatar_to_crop, 'crop_x' => 0, 'crop_y' => 0, ); if ( 'array' === $return ) { return bp_core_avatar_handle_crop( $crop_args, 'array' ); } return bp_core_avatar_handle_crop( $crop_args ); } return false; } /** * Crop an uploaded avatar. * * @since 1.1.0 * @since 10.0.0 Adds the `$return` param to eventually return the crop result. * * @param array|string $args { * Array of function parameters. * * @type string $object Object type of the item whose avatar you're * handling. 'user', 'group', 'blog', or custom. * Default: 'user'. * @type string $avatar_dir Subdirectory where avatar should be stored. * Default: 'avatars'. * @type bool|int $item_id ID of the item that the avatar belongs to. * @type bool|string $original_file Absolute path to the original avatar file. * @type int $crop_w Crop width. Default: the global 'full' avatar width, * as retrieved by bp_core_avatar_full_width(). * @type int $crop_h Crop height. Default: the global 'full' avatar height, * as retrieved by bp_core_avatar_full_height(). * @type int $crop_x The horizontal starting point of the crop. Default: 0. * @type int $crop_y The vertical starting point of the crop. Default: 0. * } * @param string $retval Optional. Whether to get the crop `array` or a `boolean`. Defaults to `boolean`. * @return array|bool */ function bp_core_avatar_handle_crop( $args = '', $retval = 'boolean' ) { $r = bp_parse_args( $args, array( 'object' => 'user', 'avatar_dir' => 'avatars', 'item_id' => false, 'original_file' => false, 'crop_w' => bp_core_avatar_full_width(), 'crop_h' => bp_core_avatar_full_height(), 'crop_x' => 0, 'crop_y' => 0, ) ); /** * Filters whether or not to handle cropping. * * If you want to override this function, make sure you return false. * * @since 1.2.4 * * @param bool $value Whether or not to crop. * @param array $r Array of parsed arguments for function. */ if ( ! apply_filters( 'bp_core_pre_avatar_handle_crop', true, $r ) ) { return true; } // Crop the file. $avatar_attachment = new BP_Attachment_Avatar(); $cropped = $avatar_attachment->crop( $r ); // Check for errors. if ( empty( $cropped['full'] ) || empty( $cropped['thumb'] ) || is_wp_error( $cropped['full'] ) || is_wp_error( $cropped['thumb'] ) ) { return false; } if ( 'array' === $retval ) { return $cropped; } return true; } /** * Ajax set an avatar for a given object and item id. * * @since 2.3.0 */ function bp_avatar_ajax_set() { if ( ! bp_is_post_request() ) { wp_send_json_error(); } // Check the nonce. check_admin_referer( 'bp_avatar_cropstore', 'nonce' ); $avatar_data = bp_parse_args( $_POST, array( 'crop_w' => bp_core_avatar_full_width(), 'crop_h' => bp_core_avatar_full_height(), 'crop_x' => 0, 'crop_y' => 0, ) ); if ( empty( $avatar_data['object'] ) || empty( $avatar_data['item_id'] ) || empty( $avatar_data['original_file'] ) ) { wp_send_json_error(); } // Sanitize object id. $item_id = (int) $avatar_data['item_id']; // Capability check. if ( ! bp_attachments_current_user_can( 'edit_avatar', $avatar_data ) ) { wp_send_json_error(); } if ( ! empty( $avatar_data['type'] ) && 'camera' === $avatar_data['type'] && 'user' === $avatar_data['object'] ) { $webcam_avatar = false; if ( ! empty( $avatar_data['original_file'] ) ) { $webcam_avatar = str_replace( array( 'data:image/png;base64,', ' ' ), array( '', '+' ), $avatar_data['original_file'] ); $webcam_avatar = base64_decode( $webcam_avatar ); } $cropped_webcam_avatar = bp_avatar_handle_capture( $webcam_avatar, $item_id, 'array' ); if ( ! $cropped_webcam_avatar ) { wp_send_json_error( array( 'feedback_code' => 1, ) ); } else { $return = array( 'avatar' => esc_url( bp_core_fetch_avatar( array( 'object' => $avatar_data['object'], 'item_id' => $item_id, 'html' => false, 'type' => 'full', ) ) ), 'feedback_code' => 2, 'item_id' => $item_id, ); /** This action is documented in wp-includes/deprecated.php */ do_action_deprecated( 'xprofile_avatar_uploaded', array( $item_id, $avatar_data['type'], $avatar_data ), '6.0.0', 'bp_members_avatar_uploaded' ); /** * Fires if the new avatar was successfully captured. * * @since 6.0.0 * @since 10.0.0 Adds a new param: an array containing the full, thumb avatar and the timestamp. * * @param string $item_id Inform about the user id the avatar was set for. * @param string $type Inform about the way the avatar was set ('camera'). * @param array $avatar_data Array of parameters passed to the crop handler. * @param array $cropped_webcam_avatar Array containing the full, thumb avatar and the timestamp. */ do_action( 'bp_members_avatar_uploaded', $item_id, $avatar_data['type'], $avatar_data, $cropped_webcam_avatar ); wp_send_json_success( $return ); } return; } $original_file = str_replace( bp_core_avatar_url(), '', $avatar_data['original_file'] ); // Set avatars dir & feedback part. if ( 'user' === $avatar_data['object'] ) { $avatar_dir = 'avatars'; // Defaults to object-avatars dir. } else { $avatar_dir = sanitize_key( $avatar_data['object'] ) . '-avatars'; } // Crop args. $r = array( 'item_id' => $item_id, 'object' => $avatar_data['object'], 'avatar_dir' => $avatar_dir, 'original_file' => $original_file, 'crop_w' => $avatar_data['crop_w'], 'crop_h' => $avatar_data['crop_h'], 'crop_x' => $avatar_data['crop_x'], 'crop_y' => $avatar_data['crop_y'], ); // Handle crop. $cropped_avatar = bp_core_avatar_handle_crop( $r, 'array' ); if ( $cropped_avatar ) { $return = array( 'avatar' => esc_url( bp_core_fetch_avatar( array( 'object' => $avatar_data['object'], 'item_id' => $item_id, 'html' => false, 'type' => 'full', ) ) ), 'feedback_code' => 2, 'item_id' => $item_id, ); if ( 'user' === $avatar_data['object'] ) { /** This action is documented in wp-includes/deprecated.php */ do_action_deprecated( 'xprofile_avatar_uploaded', array( $item_id, $avatar_data['type'], $r ), '6.0.0', 'bp_members_avatar_uploaded' ); /** This action is documented in bp-core/bp-core-avatars.php */ do_action( 'bp_members_avatar_uploaded', $item_id, $avatar_data['type'], $r, $cropped_avatar ); } elseif ( 'group' === $avatar_data['object'] ) { /** This action is documented in bp-groups/bp-groups-screens.php */ do_action( 'groups_avatar_uploaded', $item_id, $avatar_data['type'], $r, $cropped_avatar ); } wp_send_json_success( $return ); } else { wp_send_json_error( array( 'feedback_code' => 1, ) ); } } add_action( 'wp_ajax_bp_avatar_set', 'bp_avatar_ajax_set' ); /** * Filter {@link get_avatar_url()} to use the BuddyPress user avatar URL. * * @since 2.9.0 * * @param string $retval The URL of the avatar. * @param mixed $id_or_email The Gravatar to retrieve. Accepts a user_id, gravatar md5 hash, * user email, WP_User object, WP_Post object, or WP_Comment object. * @param array $args Arguments passed to get_avatar_data(), after processing. * @return string */ function bp_core_get_avatar_data_url_filter( $retval, $id_or_email, $args ) { $user = null; // Ugh, hate duplicating code; process the user identifier. if ( is_numeric( $id_or_email ) ) { $user = get_user_by( 'id', absint( $id_or_email ) ); } elseif ( $id_or_email instanceof WP_User ) { // User Object. $user = $id_or_email; } elseif ( $id_or_email instanceof WP_Post ) { // Post Object. $user = get_user_by( 'id', (int) $id_or_email->post_author ); } elseif ( $id_or_email instanceof WP_Comment ) { if ( ! empty( $id_or_email->user_id ) ) { $user = get_user_by( 'id', (int) $id_or_email->user_id ); } } elseif ( is_email( $id_or_email ) ) { $user = get_user_by( 'email', $id_or_email ); } // No user, so bail. if ( false === $user instanceof WP_User ) { return $retval; } // Set BuddyPress-specific avatar args. $args['item_id'] = $user->ID; $args['html'] = false; // Use the 'full' type if size is larger than BP's thumb width. if ( (int) $args['size'] > bp_core_avatar_thumb_width() ) { $args['type'] = 'full'; } // Get the BuddyPress avatar URL. $bp_avatar = bp_core_fetch_avatar( $args ); if ( $bp_avatar ) { return $bp_avatar; } return $retval; } add_filter( 'get_avatar_url', 'bp_core_get_avatar_data_url_filter', 10, 3 ); /** * Is the current avatar upload error-free? * * @since 1.0.0 * * @param array $file The $_FILES array. * @return bool */ function bp_core_check_avatar_upload( $file ) { if ( isset( $file['error'] ) && $file['error'] ) { return false; } return true; } /** * Is the file size of the current avatar upload permitted? * * @since 1.0.0 * * @param array $file The $_FILES array. * @return bool True if the avatar is under the size limit, otherwise false. */ function bp_core_check_avatar_size( $file ) { if ( $file['file']['size'] > bp_core_avatar_original_max_filesize() ) { return false; } return true; } /** * Get allowed avatar types. * * @since 2.3.0 * * @return array */ function bp_core_get_allowed_avatar_types() { $allowed_types = bp_attachments_get_allowed_types( 'avatar' ); /** * Filters the list of allowed image types. * * @since 2.3.0 * * @param array $allowed_types List of image types. */ $avatar_types = (array) apply_filters( 'bp_core_get_allowed_avatar_types', $allowed_types ); if ( empty( $avatar_types ) ) { $avatar_types = $allowed_types; } else { $avatar_types = array_intersect( $allowed_types, $avatar_types ); } return array_values( $avatar_types ); } /** * Get allowed avatar mime types. * * @since 2.3.0 * * @return array */ function bp_core_get_allowed_avatar_mimes() { $allowed_types = bp_core_get_allowed_avatar_types(); return bp_attachments_get_allowed_mimes( 'avatar', $allowed_types ); } /** * Does the current avatar upload have an allowed file type? * * Permitted file types are JPG, GIF and PNG. * * @since 1.0.0 * * @param array $file The $_FILES array. * @return bool True if the file extension is permitted, otherwise false. */ function bp_core_check_avatar_type( $file ) { return bp_attachments_check_filetype( $file['file']['tmp_name'], $file['file']['name'], bp_core_get_allowed_avatar_mimes() ); } /** * Fetch data from the BP root blog's upload directory. * * @since 1.8.0 * * @param string $type The variable we want to return from the $bp->avatars object. * Only 'upload_path' and 'url' are supported. Default: 'upload_path'. * @return string The avatar upload directory path. */ function bp_core_get_upload_dir( $type = 'upload_path' ) { $bp = buddypress(); $retval = ''; switch ( $type ) { case 'upload_path': $constant = 'BP_AVATAR_UPLOAD_PATH'; $key = 'basedir'; break; case 'url': $constant = 'BP_AVATAR_URL'; $key = 'baseurl'; break; default: return $retval; } // See if the value has already been calculated and stashed in the $bp global. if ( isset( $bp->avatar->$type ) ) { $retval = $bp->avatar->$type; } else { // If this value has been set in a constant, just use that. if ( defined( $constant ) ) { $retval = constant( $constant ); } else { // Use cached upload dir data if available. if ( ! empty( $bp->avatar->upload_dir ) ) { $upload_dir = $bp->avatar->upload_dir; // No cache, so query for it. } else { // Get upload directory information from current site. $upload_dir = bp_upload_dir(); // Stash upload directory data for later use. $bp->avatar->upload_dir = $upload_dir; } // Upload directory exists. if ( isset( $upload_dir[ $key ] ) ) { $retval = $upload_dir[ $key ]; // If $key is 'baseurl', check to see if we're on SSL // Workaround for WP13941, WP15928, WP19037. if ( $key === 'baseurl' && is_ssl() ) { $retval = str_replace( 'http://', 'https://', $retval ); } } } // Stash in $bp for later use. $bp->avatar->$type = $retval; } return $retval; } /** * Get the absolute upload path for the WP installation. * * @since 1.2.0 * * @return string Absolute path to WP upload directory. */ function bp_core_avatar_upload_path() { /** * Filters the absolute upload path for the WP installation. * * @since 1.2.0 * * @param string $upload_path Absolute upload path for the WP installation. */ return apply_filters( 'bp_core_avatar_upload_path', bp_core_get_upload_dir() ); } /** * Get the raw base URL for root site upload location. * * @since 1.2.0 * * @return string Full URL to current upload location. */ function bp_core_avatar_url() { /** * Filters the raw base URL for root site upload location. * * @since 1.2.0 * * @param string $avatar_url Raw base URL for the root site upload location. */ return apply_filters( 'bp_core_avatar_url', bp_core_get_upload_dir( 'url' ) ); } /** * Check if a given user ID has an uploaded avatar. * * @since 1.0.0 * * @param int $user_id ID of the user whose avatar is being checked. * @return bool True if the user has uploaded a local avatar. Otherwise false. */ function bp_get_user_has_avatar( $user_id = 0 ) { if ( empty( $user_id ) ) { $user_id = bp_displayed_user_id(); } $retval = false; if ( bp_core_fetch_avatar( array( 'item_id' => $user_id, 'no_grav' => true, 'html' => false, 'type' => 'full', ) ) !== bp_core_avatar_default( 'local' ) ) { $retval = true; } /** * Filters whether or not a user has an uploaded avatar. * * @since 1.6.0 * * @param bool $retval Whether or not a user has an uploaded avatar. * @param int $user_id ID of the user being checked. */ return (bool) apply_filters( 'bp_get_user_has_avatar', $retval, $user_id ); } /** * Utility function for fetching an avatar dimension setting. * * @since 1.5.0 * * @param string $type Dimension type you're fetching dimensions for. 'thumb' * or 'full'. Default: 'thumb'. * @param string $h_or_w Which dimension is being fetched. 'height' or 'width'. * Default: 'height'. * @return int|bool $dim The dimension. */ function bp_core_avatar_dimension( $type = 'thumb', $h_or_w = 'height' ) { $bp = buddypress(); $dim = isset( $bp->avatar->{$type}->{$h_or_w} ) ? (int) $bp->avatar->{$type}->{$h_or_w} : false; /** * Filters the avatar dimension setting. * * @since 1.5.0 * * @param int|bool $dim Dimension setting for the type. * @param string $type The type of avatar whose dimensions are requested. Default 'thumb'. * @param string $h_or_w The dimension parameter being requested. Default 'height'. */ return apply_filters( 'bp_core_avatar_dimension', $dim, $type, $h_or_w ); } /** * Get the 'thumb' avatar width setting. * * @since 1.5.0 * * @return int The 'thumb' width. */ function bp_core_avatar_thumb_width() { /** * Filters the 'thumb' avatar width setting. * * @since 1.5.0 * * @param int $avatar_thumb_width Value for the 'thumb' avatar width setting. */ return apply_filters( 'bp_core_avatar_thumb_width', bp_core_avatar_dimension( 'thumb', 'width' ) ); } /** * Get the 'thumb' avatar height setting. * * @since 1.5.0 * * @return int The 'thumb' height. */ function bp_core_avatar_thumb_height() { /** * Filters the 'thumb' avatar height setting. * * @since 1.5.0 * * @param int $avatar_thumb_height Value for the 'thumb' avatar height setting. */ return apply_filters( 'bp_core_avatar_thumb_height', bp_core_avatar_dimension( 'thumb', 'height' ) ); } /** * Get the 'full' avatar width setting. * * @since 1.5.0 * * @return int The 'full' width. */ function bp_core_avatar_full_width() { /** * Filters the 'full' avatar width setting. * * @since 1.5.0 * * @param int $avatar_full_width Value for the 'full' avatar width setting. */ return apply_filters( 'bp_core_avatar_full_width', bp_core_avatar_dimension( 'full', 'width' ) ); } /** * Get the 'full' avatar height setting. * * @since 1.5.0 * * @return int The 'full' height. */ function bp_core_avatar_full_height() { /** * Filters the 'full' avatar height setting. * * @since 1.5.0 * * @param int $avatar_full_height Value for the 'full' avatar height setting. */ return apply_filters( 'bp_core_avatar_full_height', bp_core_avatar_dimension( 'full', 'height' ) ); } /** * Get the max width for original avatar uploads. * * @since 1.5.0 * * @return int The max width for original avatar uploads. */ function bp_core_avatar_original_max_width() { /** * Filters the max width for original avatar uploads. * * @since 1.5.0 * * @param int $original_max_width Value for the max width. */ return apply_filters( 'bp_core_avatar_original_max_width', (int) buddypress()->avatar->original_max_width ); } /** * Get the max filesize for original avatar uploads. * * @since 1.5.0 * * @return int The max filesize for original avatar uploads. */ function bp_core_avatar_original_max_filesize() { /** * Filters the max filesize for original avatar uploads. * * @since 1.5.0 * * @param int $original_max_filesize Value for the max filesize. */ return apply_filters( 'bp_core_avatar_original_max_filesize', (int) buddypress()->avatar->original_max_filesize ); } /** * Get the URL of the 'full' default avatar. * * @since 1.5.0 * @since 2.6.0 Introduced `$params` and `$object_type` parameters. * * @param string $type 'local' if the fallback should be the locally-hosted version * of the mystery person, 'gravatar' if the fallback should be * Gravatar's version. Default: 'gravatar'. * @param array $params Parameters passed to bp_core_fetch_avatar(). * @return string The URL of the default avatar. */ function bp_core_avatar_default( $type = 'gravatar', $params = array() ) { // Local override. if ( defined( 'BP_AVATAR_DEFAULT' ) ) { $avatar = BP_AVATAR_DEFAULT; // Use the local default image. } elseif ( 'local' === $type ) { $size = ''; if ( ( isset( $params['type'] ) && 'thumb' === $params['type'] && bp_core_avatar_thumb_width() <= 50 ) || ( isset( $params['width'] ) && $params['width'] <= 50 ) ) { $size = '-50'; } $avatar = buddypress()->plugin_url . "bp-core/images/mystery-man{$size}.jpg"; // Use Gravatar's mystery person as fallback. } else { $size = ''; if ( isset( $params['type'] ) && 'thumb' === $params['type'] ) { $size = bp_core_avatar_thumb_width(); } else { $size = bp_core_avatar_full_width(); } $avatar = '//www.gravatar.com/avatar/00000000000000000000000000000000?d=mm&s=' . $size; } /** This filter is documented in wp-includes/deprecated.php */ $a = apply_filters_deprecated( 'bp_core_avatar_default', array( $avatar, $params ), '8.0.0', 'bp_core_avatar_gravatar_default||bp_core_default_avatar', __( 'This filter was used for 2 different purposes. If your goal was to filter the default *Gravatar*, please use `bp_core_avatar_gravatar_default` instead. Otherwise, please use `bp_core_default_avatar` instead.', 'buddypress' ) ); if ( ! bp_core_is_default_gravatar( $a ) && false !== strpos( $avatar, '//' ) ) { $avatar = $a; } /** * Filters the URL of the 'full' default avatar. * * @since 1.5.0 * @since 2.6.0 Added `$params`. * @since 8.0.0 The name of the filter was changed to `bp_core_default_avatar`. * * @param string $avatar URL of the default avatar. * @param array $params Params provided to bp_core_fetch_avatar(). */ return apply_filters( 'bp_core_default_avatar', $avatar, $params ); } /** * Get the URL of the 'thumb' default avatar. * * Uses Gravatar's mystery-person avatar, unless BP_AVATAR_DEFAULT_THUMB has been * defined. * * @since 1.5.0 * @since 2.6.0 Introduced `$object_type` parameter. * * @param string $type 'local' if the fallback should be the locally-hosted version * of the mystery person, 'gravatar' if the fallback should be * Gravatar's version. Default: 'gravatar'. * @param array $params Parameters passed to bp_core_fetch_avatar(). * @return string The URL of the default avatar thumb. */ function bp_core_avatar_default_thumb( $type = 'gravatar', $params = array() ) { // Local override. if ( defined( 'BP_AVATAR_DEFAULT_THUMB' ) ) { $avatar = BP_AVATAR_DEFAULT_THUMB; // Use the local default image. } elseif ( 'local' === $type ) { $avatar = buddypress()->plugin_url . 'bp-core/images/mystery-man-50.jpg'; // Use Gravatar's mystery person as fallback. } else { $avatar = '//www.gravatar.com/avatar/00000000000000000000000000000000?d=mm&s=' . bp_core_avatar_thumb_width(); } /** * Filters the URL of the 'thumb' default avatar. * * @since 1.5.0 * @since 2.6.0 Added `$params`. * * @param string $avatar URL of the default avatar. * @param string $params Params provided to bp_core_fetch_avatar(). */ return apply_filters( 'bp_core_avatar_thumb', $avatar, $params ); } /** * Reset the week parameter of the WordPress main query if needed. * * When cropping an avatar, a $_POST['w'] var is sent, setting the 'week' * parameter of the WordPress main query to this posted var. To avoid * notices, we need to make sure this 'week' query var is reset to 0. * * @since 2.2.0 * * @param WP_Query|null $posts_query The main query object. */ function bp_core_avatar_reset_query( $posts_query = null ) { $reset_w = false; // Group's avatar edit screen. if ( bp_is_group_admin_page() ) { $reset_w = bp_is_group_admin_screen( 'group-avatar' ); // Group's avatar create screen. } elseif ( bp_is_group_create() ) { /** * We can't use bp_get_groups_current_create_step(). * as it's not set yet */ $reset_w = 'group-avatar' === bp_action_variable( 1 ); // User's change avatar screen. } else { $reset_w = bp_is_user_change_avatar(); } // A user or a group is cropping an avatar. if ( true === $reset_w && isset( $_POST['avatar-crop-submit'] ) ) { $posts_query->set( 'w', 0 ); } } add_action( 'bp_parse_query', 'bp_core_avatar_reset_query', 10, 1 ); /** * Checks whether Avatar UI should be loaded. * * @since 2.3.0 * * @return bool True if Avatar UI should load, false otherwise. */ function bp_avatar_is_front_edit() { $retval = false; if ( bp_is_user_change_avatar() && 'crop-image' !== bp_get_avatar_admin_step() ) { $retval = ! bp_core_get_root_option( 'bp-disable-avatar-uploads' ); } if ( bp_is_active( 'groups' ) ) { // Group creation. if ( bp_is_group_create() && bp_is_group_creation_step( 'group-avatar' ) && 'crop-image' !== bp_get_avatar_admin_step() ) { $retval = ! bp_disable_group_avatar_uploads(); // Group Manage. } elseif ( bp_is_group_admin_page() && bp_is_group_admin_screen( 'group-avatar' ) && 'crop-image' !== bp_get_avatar_admin_step() ) { $retval = ! bp_disable_group_avatar_uploads(); } } /** * Use this filter if you need to : * - Load the avatar UI for a component that is !groups or !user (return true regarding your conditions) * - Completely disable the avatar UI introduced in 2.3 (eg: __return_false()) * * @since 2.3.0 * * @param bool $retval Whether or not to load the Avatar UI. */ return apply_filters( 'bp_avatar_is_front_edit', $retval ); } /** * Checks whether the Webcam Avatar UI part should be loaded. * * @since 2.3.0 * * @global bool $is_safari * @global bool $is_IE * * @return bool True to load the Webcam Avatar UI part. False otherwise. */ function bp_avatar_use_webcam() { global $is_safari, $is_IE, $is_chrome; /** * Do not use the webcam feature for mobile devices * to avoid possible confusions. */ if ( wp_is_mobile() ) { return false; } /** * Bail when the browser does not support getUserMedia. * * @see http://caniuse.com/#feat=stream */ if ( $is_safari || $is_IE || ( $is_chrome && ! is_ssl() ) ) { return false; } /** * Use this filter if you need to disable the webcam capture feature * by returning false. * * @since 2.3.0 * * @param bool $value Whether or not to load Webcam Avatar UI part. */ return apply_filters( 'bp_avatar_use_webcam', true ); } /** * Template function to load the Avatar UI javascript templates. * * @since 2.3.0 */ function bp_avatar_get_templates() { if ( ! bp_avatar_is_front_edit() ) { return; } bp_attachments_get_template_part( 'avatars/index' ); } /** * Trick to check if the theme's BuddyPress templates are up to date. * * If the "avatar templates" are not including the new template tag, this will * help users to get the avatar UI. * * @since 2.3.0 */ function bp_avatar_template_check() { if ( ! bp_avatar_is_front_edit() ) { return; } if ( ! did_action( 'bp_attachments_avatar_check_template' ) ) { bp_attachments_get_template_part( 'avatars/index' ); } } /** * Informs about whether avatar history is disabled or not. * * @since 10.0.0 * * @return bool True if avatar history is disabled. False otherwise. * Default: `false`. */ function bp_avatar_history_is_disabled() { /** * Filter here returning `true` to disable avatar history. * * @since 10.0.0 * * @param bool $avatar_history True to disable avatar history. False otherwise. * Default: `false`. */ return apply_filters( 'bp_disable_avatar_history', false ); } /** * Get a specific version of an avatar from its history. * * @since 10.0.0 * * @param int $item_id The item ID we need the avatar version for. * @param string $object The object the item ID relates to. * @param int|string $timestamp An integer Unix timestamp or a date string of the format 'Y-m-d h:i:s'. * @param string $type The type of avatar we need. Possible values are `thumb` and `full`. * @return array A list of matching results, an empty array if no avatars were found. */ function bp_avatar_get_version( $item_id = 0, $object = 'user', $timestamp = '', $type = 'full' ) { if ( ! $item_id || ! $timestamp ) { return array(); } if ( ! is_numeric( $timestamp ) ) { $timestamp = strtotime( $timestamp ); } $avatar_id = $timestamp . '-bpfull'; if ( 'full' !== $type ) { $avatar_id = $timestamp . '-bpthumb'; } $avatar_dir = 'avatars'; if ( 'user' !== $object ) { $avatar_dir = sanitize_key( $object ) . '-avatars'; } // The object avatar directory we are looking into to get the avatar url. $object_avatar_dir = trailingslashit( bp_core_avatar_upload_path() ) . $avatar_dir . '/' . $item_id; return bp_attachments_list_directory_files_recursively( $object_avatar_dir, $avatar_id ); } /** * Get the list of previous avatars in history * * @since 10.0.0 * * @param int $item_id The item ID we need the avatar version for. * @param string $object The object the item ID relates to. * @param string $type Get the `full`, `thumb` or `both` versions. * @return array The list of previous uploaded avatars. */ function bp_avatar_get_avatars_history( $item_id = 0, $object = 'user', $type = 'full' ) { if ( ! $item_id ) { return array(); } $avatar_dir = 'avatars'; if ( 'user' !== $object ) { $avatar_dir = sanitize_key( $object ) . '-avatars'; } // The user avatar directory we are looking into to get the avatar url. $history_dir = trailingslashit( bp_core_avatar_upload_path() ) . $avatar_dir . '/' . $item_id . '/history'; $historic_avatars = bp_attachments_list_directory_files( $history_dir ); if ( ! $historic_avatars ) { return array(); } $avatars = array(); $history_url = trailingslashit( bp_core_avatar_url() ) . $avatar_dir . '/' . $item_id . '/history'; foreach ( $historic_avatars as $historic_avatar ) { $prefix = str_replace( array( '-bpfull', '-bpthumb' ), '', $historic_avatar->id ); $gmdate = gmdate( 'Y-m-d H:i:s', $historic_avatar->last_modified ); $date = strtotime( get_date_from_gmt( $gmdate ) ); $avatars[ $historic_avatar->id ] = (object) array( 'id' => $historic_avatar->id, 'name' => $historic_avatar->name, 'date' => sprintf( '%1$s (%2$s)', date_i18n( get_option( 'date_format' ), $date ), date_i18n( get_option( 'time_format' ), $date ) ), 'type' => str_replace( $prefix . '-bp', '', $historic_avatar->id ), 'url' => $history_url . '/' . $historic_avatar->name, ); } if ( 'both' === $type ) { return $avatars; } return wp_filter_object_list( $avatars, array( 'type' => $type ) ); } /** * Recycle a previously uploaded avatar as the current avatar. * * @since 10.0.0 */ function bp_avatar_ajax_recycle_previous_avatar() { if ( ! bp_is_post_request() ) { wp_send_json_error(); } $avatar_data = bp_parse_args( $_POST, array( 'object' => '', 'item_id' => 0, 'avatar_id' => '', ) ); if ( ! $avatar_data['object'] || ! $avatar_data['item_id'] || ! $avatar_data['avatar_id'] ) { wp_send_json_error(); } // Check the nonce. check_admin_referer( 'bp_avatar_recycle_previous', 'nonce' ); // Capability check. if ( ! bp_attachments_current_user_can( 'edit_avatar', $avatar_data ) ) { wp_send_json_error(); } // Set the Avatar Attachment Instance. $avatar_attachment = new BP_Attachment_Avatar(); $object = sanitize_key( $avatar_data['object'] ); if ( 'user' === $object ) { $avatar_dir = 'avatars'; } else { $avatar_dir = $object . '-avatars'; } $item_id = (int) $avatar_data['item_id']; $avatar_dir_path = $avatar_attachment->upload_path . '/' . $avatar_dir . '/' . $item_id; $current_avatars = bp_attachments_list_directory_files( $avatar_dir_path ); $revision_errors = array(); // This path will be needed to get the avatar revision object. $avatar_full_revision_path = ''; // Add a revision of the current avatar if it's not a mystery man! if ( $current_avatars ) { foreach ( $current_avatars as $current_avatar ) { if ( ! isset( $current_avatar->name, $current_avatar->id, $current_avatar->path ) ) { continue; } $is_full = preg_match( '/-bpfull/', $current_avatar->name ); $is_thumb = preg_match( '/-bpthumb/', $current_avatar->name ); if ( $is_full || $is_thumb ) { // Add a revision of the current avatar. $revision = $avatar_attachment->add_revision( 'avatar', array( 'file_abspath' => $current_avatar->path, 'file_id' => $current_avatar->id, ) ); if ( is_wp_error( $revision ) ) { $revision_errors[] = $revision->get_error_message(); } elseif ( $is_full ) { $avatar_full_revision_path = $revision->path; } } } } // No errors, let's recycle the previous avatar. if ( ! $revision_errors ) { $avatar_id = sanitize_file_name( $avatar_data['avatar_id'] ); $suffix = '-bpfull'; $history_dir = trailingslashit( bp_core_avatar_upload_path() ) . $avatar_dir . '/' . $item_id . '/history'; $avatars = bp_attachments_list_directory_files( $history_dir ); if ( ! isset( $avatars[ $avatar_id ] ) ) { wp_send_json_error( array( 'message' => __( 'The profile photo you want to recycle cannot be found.', 'buddypress' ), ) ); } // Init recycle vars. $recycle_timestamp = bp_core_current_time( true, 'timestamp' ); $recycle_errors = array(); $avatar_types = array( 'full' => '', 'thumb' => '', ); // Use the found previous avatar. $avatar = $avatars[ $avatar_id ]; $avatar_types['full'] = $avatar->path; $avatar_types['thumb'] = str_replace( $suffix, '-bpthumb', $avatar->path ); $historical_types = $avatar_types; // It's a legacy avatar, we need to crop it again and remove the thumb file that is not using a timestamp in its name. if ( ! file_exists( $avatar_types['thumb'] ) ) { $full_avatar_path = $avatar_dir_path . '/' . str_replace( 'bpfull', 'original-file', wp_basename( $avatar->path ) ); // Move the full version back to avatar dir. rename( $avatar->path, $full_avatar_path ); $avatar_types = $avatar_attachment->crop( array( 'original_file' => $full_avatar_path, 'avatar_dir' => $avatar_dir, 'object' => $object, 'item_id' => $item_id, ) ); // loop into the history directory to delete the legacy thumb version file. foreach ( $avatars as $avatar_object ) { $timestamp = str_replace( array( '-bpthumb', '-bpfull' ), '', $avatar_object->id ); if ( ! is_numeric( $timestamp ) && false !== strpos( $avatar_object->id, '-bpthumb' ) ) { @unlink( $avatar_object->path ); } } } else { foreach ( $avatar_types as $type_key => $avatar_path ) { $filename = wp_basename( $avatar_path ); $avatar_id = pathinfo( $filename, PATHINFO_FILENAME ); $recycle_path = $avatar_dir_path . '/' . str_replace( $avatar_id, $recycle_timestamp . '-bp' . $type_key, $filename ); if ( ! rename( $avatar_path, $recycle_path ) ) { $recycle_errors[] = __( 'An unexpected error occured while recycling the previous profile photo.', 'buddypress' ); } else { $avatar_types[ $type_key ] = $recycle_path; } } $avatar_types = array_merge( $avatar_types, array( 'timestamp' => $recycle_timestamp, ) ); } // No errors, fire the hook used when an avatar is set. if ( ! $recycle_errors && $historical_types['full'] !== $avatar_types['full'] ) { $r = array( 'item_id' => $item_id, 'object' => $object, 'avatar_dir' => $avatar_dir, ); $action_hook = 'bp_members_avatar_uploaded'; if ( 'group' === $object ) { $action_hook = 'groups_avatar_uploaded'; } /** This action is documented in bp-core/bp-core-avatars.php */ do_action( $action_hook, $item_id, 'recycle', $r, $avatar_types ); } else { $recycle_error = reset( $recycle_errors ); wp_send_json_error( array( 'message' => join( "\n", $recycle_error ), ) ); } } else { wp_send_json_error( array( 'message' => join( "\n", $revision_errors ), ) ); } $return = array( 'avatar' => esc_url( bp_core_fetch_avatar( array( 'object' => $object, 'item_id' => $item_id, 'html' => false, 'type' => 'full', ) ) ), 'feedback_code' => 5, 'item_id' => $item_id, ); // Get the created revision object if it exists. if ( $avatar_full_revision_path ) { $history_dir = dirname( $avatar_full_revision_path ); $avatars_history = bp_attachments_list_directory_files( $history_dir ); $latest_id = pathinfo( wp_basename( $avatar_full_revision_path ), PATHINFO_FILENAME ); if ( isset( $avatars_history[ $latest_id ] ) ) { $gmdate = gmdate( 'Y-m-d H:i:s', $avatars_history[ $latest_id ]->last_modified ); $date = strtotime( get_date_from_gmt( $gmdate ) ); $history_url = trailingslashit( bp_core_avatar_url() ) . $avatar_dir . '/' . $item_id . '/history'; // Prepare the avatar object for JavaScript. $avatars_history[ $latest_id ]->date = sprintf( '%1$s (%2$s)', date_i18n( get_option( 'date_format' ), $date ), date_i18n( get_option( 'time_format' ), $date ) ); $avatars_history[ $latest_id ]->type = 'full'; $avatars_history[ $latest_id ]->url = $history_url . '/' . $avatars_history[ $latest_id ]->name; // Remove the path. unset( $avatars_history[ $latest_id ]->path ); // Set the new object to add to the revision list. $return['historicalAvatar'] = $avatars_history[ $latest_id ]; } } wp_send_json_success( $return ); } add_action( 'wp_ajax_bp_avatar_recycle_previous', 'bp_avatar_ajax_recycle_previous_avatar' ); /** * Delete a previously uploaded avatar from avatars history. * * @since 10.0.0 */ function bp_avatar_ajax_delete_previous_avatar() { if ( ! bp_is_post_request() ) { wp_send_json_error(); } $avatar_data = bp_parse_args( $_POST, array( 'object' => '', 'item_id' => 0, 'avatar_id' => '', ) ); if ( ! $avatar_data['object'] || ! $avatar_data['item_id'] || ! $avatar_data['avatar_id'] ) { wp_send_json_error(); } // Check the nonce. check_admin_referer( 'bp_avatar_delete_previous', 'nonce' ); // Capability check. if ( ! bp_attachments_current_user_can( 'edit_avatar', $avatar_data ) ) { wp_send_json_error(); } $object = sanitize_key( $avatar_data['object'] ); if ( 'user' === $object ) { $avatar_dir = 'avatars'; } else { $avatar_dir = $object . '-avatars'; } $item_id = (int) $avatar_data['item_id']; $avatar_id = sanitize_file_name( $avatar_data['avatar_id'] ); $suffix = '-bpfull'; $history_dir = trailingslashit( bp_core_avatar_upload_path() ) . $avatar_dir . '/' . $item_id . '/history'; $avatars = bp_attachments_list_directory_files( $history_dir ); if ( ! isset( $avatars[ $avatar_id ] ) ) { wp_send_json_error( array( 'message' => __( 'The profile photo you want to delete cannot be found.', 'buddypress' ), ) ); } $avatar_types = array( 'full' => '', 'thumb' => '', ); // Use the found previous avatar. $avatar = $avatars[ $avatar_id ]; $avatar_types['full'] = $avatar->path; $avatar_types['thumb'] = str_replace( $suffix, '-bpthumb', $avatar->path ); // It's a legacy avatar, we need to find the thumb version using file last modified date. if ( ! file_exists( $avatar_types['thumb'] ) ) { $avatar_types['thumb'] = ''; $possible_thumb_avatars = wp_list_pluck( $avatars, 'last_modified', 'id' ); foreach ( $possible_thumb_avatars as $type_id => $modified_date ) { $timediff = $avatar->last_modified - $modified_date; if ( 1000 >= absint( $timediff ) && $avatar_id !== $type_id ) { $avatar_types['thumb'] = $avatars[ $type_id ]->path; break; } } } // Remove the files. foreach ( $avatar_types as $avatar_path ) { if ( ! $avatar_path ) { continue; } @unlink( $avatar_path ); } $timestamp = str_replace( '-bpfull', '', $avatar_id ); if ( ! is_numeric( $timestamp ) ) { $timestamp = $avatar->last_modified; } /** * Hook here to run custom code once the previous avatar has been deleted. * * @since 10.0.0 * * @param int $item_id The object ID. * @param int $timestamp The avatar timestamp. */ do_action( "bp_previous_{$object}_avatar_deleted", $item_id, $timestamp ); // Finally inform about the deletion success. wp_send_json_success( array( 'feedback_code' => 6, ) ); } add_action( 'wp_ajax_bp_avatar_delete_previous', 'bp_avatar_ajax_delete_previous_avatar' ); /** * Register Avatar ajax actions. * * @since 12.0.0 */ function bp_avatar_register_ajax_actions() { $ajax_actions = array( 'bp_avatar_upload', 'bp_avatar_set', 'bp_avatar_delete', 'bp_avatar_recycle_previous', 'bp_avatar_delete_previous' ); foreach ( $ajax_actions as $ajax_action ) { bp_ajax_register_action( $ajax_action ); } } add_action( 'bp_init', 'bp_avatar_register_ajax_actions' );