Skip to:
Content

BuddyPress.org

Ticket #8581: 8581-b.patch

File 8581-b.patch, 31.8 KB (added by imath, 2 years ago)
  • src/bp-activity/bp-activity-filters.php

    diff --git src/bp-activity/bp-activity-filters.php src/bp-activity/bp-activity-filters.php
    index 3367e75f2..be6c55f2a 100644
    function bp_activity_register_personal_data_exporter( $exporters ) { 
    841841
    842842        return $exporters;
    843843}
     844
     845/**
     846 * Generate a content body for specific activity types.
     847 *
     848 * @since 10.0.0
     849 *
     850 * @param string               $content  The content of the activity.
     851 * @param BP_Activity_Activity $activity The activity object.
     852 * @return string                        The content of the activity.
     853 */
     854function bp_activity_generate_content_body( $content = '', $activity = null ) {
     855        if ( isset( $activity->type ) && 'new_avatar' === $activity->type ) {
     856                $historical_avatar = '';
     857
     858                if ( isset( $activity->historical_avatar ) && $activity->historical_avatar ) {
     859                        $historical_avatar = $activity->historical_avatar;
     860                } else {
     861                        $avatars = bp_avatar_get_version_src( $activity->user_id, $activity->date_recorded );
     862
     863                        if ( $avatars && 1 === count( $avatars ) ) {
     864                                $avatar            = reset( $avatars );
     865                                $historical_avatar = trailingslashit( $avatar->parent_dir_url ) . $avatar->name;
     866                        }
     867                }
     868
     869                if ( $historical_avatar ) {
     870                        $content = '<img src="' . esc_url( $historical_avatar ) . '" class="profile-photo aligncenter">';
     871                }
     872        }
     873
     874        return $content;
     875}
     876add_filter( 'bp_get_activity_content_body', 'bp_activity_generate_content_body', 50, 2 );
  • src/bp-activity/bp-activity-template.php

    diff --git src/bp-activity/bp-activity-template.php src/bp-activity/bp-activity-template.php
    index 16ee47d86..d39746f06 100644
    function bp_activity_content_body() { 
    14301430function bp_activity_has_content() {
    14311431        global $activities_template;
    14321432
    1433         if ( ! empty( $activities_template->activity->content ) ) {
    1434                 return true;
     1433        $has_content = ! empty( $activities_template->activity->content );
     1434        if ( ! $has_content ) {
     1435                if ( 'new_avatar' === bp_get_activity_type() ) {
     1436                        //var_dump( $activities_template );
     1437                        $avatars = bp_avatar_get_version_src( bp_get_activity_user_id(), bp_get_activity_date_recorded() );
     1438
     1439                        if ( $avatars && 1 === count( $avatars ) ) {
     1440                                $avatar            = reset( $avatars );
     1441                                $historical_avatar = trailingslashit( $avatar->parent_dir_url ) . $avatar->name;
     1442
     1443                                // Add historical avatar to the current activity.
     1444                                $activities_template->activity->historical_avatar = $historical_avatar;
     1445
     1446                                // Update the corresponding entry into the activities template global.
     1447                                $activity_id    = $activities_template->activity->id;
     1448                                $activity_index = 0;
     1449                                while ( (int) $activities_template->activities[ $activity_index ]->id !== (int) $activity_id ) {
     1450                                        $activity_index++;
     1451                                }
     1452
     1453                                $activities_template->activities[ $activity_index ]->historical_avatar = $historical_avatar;
     1454
     1455                                // Force the content to be displayed.
     1456                                $has_content = true;
     1457                        }
     1458                }
    14351459        }
    14361460
    1437         return false;
     1461        return $has_content;
    14381462}
    14391463
    14401464/**
  • src/bp-core/bp-core-attachments.php

    diff --git src/bp-core/bp-core-attachments.php src/bp-core/bp-core-attachments.php
    index e99ed0c14..a74069de5 100644
    function bp_attachments_create_item_type( $type = 'avatar', $args = array() ) { 
    409409
    410410        // It's an avatar, we need to crop it.
    411411        if ( 'avatar' === $type ) {
    412                 $created = bp_core_avatar_handle_crop( array(
    413                         'object'        => $r['object'],
    414                         'avatar_dir'    => trim( dirname( $attachment_data['subdir'] ), '/' ),
    415                         'item_id'       => (int) $r['item_id'],
    416                         'original_file' => trailingslashit( $attachment_data['subdir'] ) . $image_file_name,
    417                         'crop_w'        => $r['crop_w'],
    418                         'crop_h'        => $r['crop_h'],
    419                         'crop_x'        => $r['crop_x'],
    420                         'crop_y'        => $r['crop_y']
    421                 ) );
     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                );
    422424
    423425        // It's a cover image we need to fit it to feature's dimensions.
    424426        } elseif ( 'cover_image' === $type ) {
    425                 $cover_image = bp_attachments_cover_image_generate_file( array(
    426                         'file'            => $image_file_path,
    427                         'component'       => $r['component'],
    428                         'cover_image_dir' => $attachment_data['path']
    429                 ) );
     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                );
    430434
    431435                $created = ! empty( $cover_image['cover_file'] );
    432436        }
    function bp_attachments_cover_image_ajax_delete() { 
    15971601        }
    15981602}
    15991603add_action( 'wp_ajax_bp_cover_image_delete', 'bp_attachments_cover_image_ajax_delete' );
     1604
     1605/**
     1606 * List the files of a directory.
     1607 *
     1608 * @since 10.0.0
     1609 *
     1610 * @param string $directory_path Absolute path of a directory.
     1611 * @return array The list of the files inside the directory.
     1612 */
     1613function bp_attachments_list_directory_files( $directory_path = '' ) {
     1614        if ( ! is_dir( $directory_path ) ) {
     1615                return array();
     1616        }
     1617
     1618        $files    = array();
     1619        $iterator = new FilesystemIterator( $directory_path, FilesystemIterator::SKIP_DOTS );
     1620
     1621        foreach ( $iterator as $file ) {
     1622                $_file = new stdClass();
     1623
     1624                $_file->name               = $file->getfilename();
     1625                $_file->path               = $file->getPathname();
     1626                $_file->size               = $file->getSize();
     1627                $_file->type               = $file->getType();
     1628                $_file->mime_type          = mime_content_type( $_file->path );
     1629                $_file->last_modified      = $file->getMTime();
     1630                $_file->latest_access_date = $file->getATime();
     1631                $_file->id                 = pathinfo( $_file->name, PATHINFO_FILENAME );
     1632                $files[ $_file->id ]       = $_file;
     1633        }
     1634
     1635        return $files;
     1636}
     1637
     1638/**
     1639 * List the files of a directory recursively and eventually find a file using its ID.
     1640 *
     1641 * @since 10.0.0
     1642 *
     1643 * @param string $directory_path Absolute path of a directory.
     1644 * @param string $find           The file ID to find into the directory or its children.
     1645 * @return array The list of the files.
     1646 */
     1647function bp_attachments_list_directory_files_recursively( $directory_path = '', $find = '' ) {
     1648        if ( ! is_dir( $directory_path ) ) {
     1649                return array();
     1650        }
     1651
     1652        $files     = array();
     1653        $directory = new RecursiveDirectoryIterator( $directory_path, FilesystemIterator::SKIP_DOTS );
     1654        $iterator  = new RecursiveIteratorIterator( $directory, RecursiveIteratorIterator::CHILD_FIRST );
     1655        $bp_upload = bp_upload_dir();
     1656
     1657        foreach ( $iterator as $file ) {
     1658                $_file = new stdClass();
     1659
     1660                $_file->name               = $file->getfilename();
     1661                $_file->path               = $file->getPathname();
     1662                $_file->size               = $file->getSize();
     1663                $_file->type               = $file->getType();
     1664                $_file->mime_type          = mime_content_type( $_file->path );
     1665                $_file->last_modified      = $file->getMTime();
     1666                $_file->latest_access_date = $file->getATime();
     1667                $_file->parent_dir_path    = dirname( $_file->path );
     1668                $_file->parent_dir_url     = str_replace( $bp_upload['basedir'], $bp_upload['baseurl'], $_file->parent_dir_path );
     1669                $_file->id                 = pathinfo( $_file->name, PATHINFO_FILENAME );
     1670
     1671                // Ensure URL is https if SSL is set/forced.
     1672                if ( is_ssl() ) {
     1673                        $_file->parent_dir_url = str_replace( 'http://', 'https://', $_file->parent_dir_url );
     1674                }
     1675
     1676                $file_id = $_file->id;
     1677                if ( $_file->parent_dir_path !== $directory_path ) {
     1678                        $file_id = trailingslashit( str_replace( trailingslashit( $directory_path ), '', $_file->parent_dir_path ) ) . $file_id;
     1679                }
     1680
     1681                $files[ $file_id ] = $_file;
     1682        }
     1683
     1684        if ( $find ) {
     1685                return wp_filter_object_list( $files, array( 'id' => $find ) );
     1686        }
     1687
     1688        return $files;
     1689}
  • src/bp-core/bp-core-avatars.php

    diff --git src/bp-core/bp-core-avatars.php src/bp-core/bp-core-avatars.php
    index 301c6bfdc..fad91b34f 100644
    add_action( 'wp_ajax_bp_avatar_upload', 'bp_avatar_ajax_upload' ); 
    11761176 * Handle avatar webcam capture.
    11771177 *
    11781178 * @since 2.3.0
     1179 * @since 10.0.0 Adds the `$return` param to eventually return the crop result.
    11791180 *
    11801181 * @param string $data    Base64 encoded image.
    11811182 * @param int    $item_id Item to associate.
    1182  * @return bool True on success, false on failure.
     1183 * @param string $return  Whether to get the crop `array` or a `boolean`. Defaults to `boolean`.
     1184 * @return array|bool True on success, false on failure.
    11831185 */
    1184 function bp_avatar_handle_capture( $data = '', $item_id = 0 ) {
     1186function bp_avatar_handle_capture( $data = '', $item_id = 0, $return = 'boolean' ) {
    11851187        if ( empty( $data ) || empty( $item_id ) ) {
    11861188                return false;
    11871189        }
    function bp_avatar_handle_capture( $data = '', $item_id = 0 ) { 
    12371239                // Crop to default values.
    12381240                $crop_args = array( 'item_id' => $item_id, 'original_file' => $avatar_to_crop, 'crop_x' => 0, 'crop_y' => 0 );
    12391241
     1242                if ( 'array' === $return ) {
     1243                        return bp_core_avatar_handle_crop( $crop_args, 'array' );
     1244                }
     1245
    12401246                return bp_core_avatar_handle_crop( $crop_args );
    12411247        } else {
    12421248                return false;
    function bp_avatar_handle_capture( $data = '', $item_id = 0 ) { 
    12471253 * Crop an uploaded avatar.
    12481254 *
    12491255 * @since 1.1.0
     1256 * @since 10.0.0 Adds the `$return` param to eventually return the crop result.
    12501257 *
    12511258 * @param array|string $args {
    12521259 *     Array of function parameters.
    function bp_avatar_handle_capture( $data = '', $item_id = 0 ) { 
    12651272 *     @type int         $crop_x        The horizontal starting point of the crop. Default: 0.
    12661273 *     @type int         $crop_y        The vertical starting point of the crop. Default: 0.
    12671274 * }
    1268  * @return bool True on success, false on failure.
     1275 * @param string       $return Whether to get the crop `array` or a `boolean`. Defaults to `boolean`.
     1276 * @return array|bool True or the crop result on success, false on failure.
    12691277 */
    1270 function bp_core_avatar_handle_crop( $args = '' ) {
     1278function bp_core_avatar_handle_crop( $args = '', $return = 'boolean' ) {
    12711279
    12721280        $r = bp_parse_args(
    12731281                $args,
    function bp_core_avatar_handle_crop( $args = '' ) { 
    13061314                return false;
    13071315        }
    13081316
     1317        if ( 'array' === $return ) {
     1318                return $cropped;
     1319        }
     1320
    13091321        return true;
    13101322}
    13111323
    function bp_avatar_ajax_set() { 
    13521364                        $webcam_avatar = base64_decode( $webcam_avatar );
    13531365                }
    13541366
    1355                 if ( ! bp_avatar_handle_capture( $webcam_avatar, $avatar_data['item_id'] ) ) {
     1367                $cropped_webcam_avatar = bp_avatar_handle_capture( $webcam_avatar, $avatar_data['item_id'], 'array' );
     1368
     1369                if ( ! $cropped_webcam_avatar ) {
    13561370                        wp_send_json_error( array(
    13571371                                'feedback_code' => 1
    13581372                        ) );
    13591373
    13601374                } else {
    13611375                        $return = array(
    1362                                 'avatar' => esc_url( bp_core_fetch_avatar( array(
    1363                                         'object'  => $avatar_data['object'],
    1364                                         'item_id' => $avatar_data['item_id'],
    1365                                         'html'    => false,
    1366                                         'type'    => 'full',
    1367                                 ) ) ),
     1376                                'avatar' => esc_url(
     1377                                        bp_core_fetch_avatar(
     1378                                                array(
     1379                                                        'object'  => $avatar_data['object'],
     1380                                                        'item_id' => $avatar_data['item_id'],
     1381                                                        'html'    => false,
     1382                                                        'type'    => 'full',
     1383                                                )
     1384                                        )
     1385                                ),
    13681386                                'feedback_code' => 2,
    13691387                                'item_id'       => $avatar_data['item_id'],
    13701388                        );
    function bp_avatar_ajax_set() { 
    13761394                         * Fires if the new avatar was successfully captured.
    13771395                         *
    13781396                         * @since 6.0.0
     1397                         * @since 10.0.0 Adds a new param: an array containing the full, thumb avatar and the timestamp.
    13791398                         *
    1380                          * @param string $item_id     Inform about the user id the avatar was set for.
    1381                          * @param string $type        Inform about the way the avatar was set ('camera').
    1382                          * @param array  $avatar_data Array of parameters passed to the avatar handler.
     1399                         * @param string $item_id               Inform about the user id the avatar was set for.
     1400                         * @param string $type                  Inform about the way the avatar was set ('camera').
     1401                         * @param array  $avatar_data           Array of parameters passed to the crop handler.
     1402                         * @param array  $cropped_webcam_avatar Array containing the full, thumb avatar and the timestamp.
    13831403                         */
    1384                         do_action( 'bp_members_avatar_uploaded', (int) $avatar_data['item_id'], $avatar_data['type'], $avatar_data );
     1404                        do_action( 'bp_members_avatar_uploaded', (int) $avatar_data['item_id'], $avatar_data['type'], $avatar_data, $cropped_webcam_avatar );
    13851405
    13861406                        wp_send_json_success( $return );
    13871407                }
    function bp_avatar_ajax_set() { 
    14131433        );
    14141434
    14151435        // Handle crop.
    1416         if ( bp_core_avatar_handle_crop( $r ) ) {
     1436        $cropped_avatar = bp_core_avatar_handle_crop( $r, 'array' );
     1437
     1438        if ( $cropped_avatar ) {
    14171439                $return = array(
    1418                         'avatar' => esc_url( bp_core_fetch_avatar( array(
    1419                                 'object'  => $avatar_data['object'],
    1420                                 'item_id' => $avatar_data['item_id'],
    1421                                 'html'    => false,
    1422                                 'type'    => 'full',
    1423                         ) ) ),
     1440                        'avatar' => esc_url(
     1441                                bp_core_fetch_avatar(
     1442                                        array(
     1443                                                'object'  => $avatar_data['object'],
     1444                                                'item_id' => $avatar_data['item_id'],
     1445                                                'html'    => false,
     1446                                                'type'    => 'full',
     1447                                        )
     1448                                )
     1449                        ),
    14241450                        'feedback_code' => 2,
    14251451                        'item_id'       => $avatar_data['item_id'],
    14261452                );
    function bp_avatar_ajax_set() { 
    14301456                        do_action_deprecated( 'xprofile_avatar_uploaded', array( (int) $avatar_data['item_id'], $avatar_data['type'], $r ), '6.0.0', 'bp_members_avatar_uploaded' );
    14311457
    14321458                        /** This action is documented in bp-core/bp-core-avatars.php */
    1433                         do_action( 'bp_members_avatar_uploaded', (int) $avatar_data['item_id'], $avatar_data['type'], $r );
     1459                        do_action( 'bp_members_avatar_uploaded', (int) $avatar_data['item_id'], $avatar_data['type'], $r, $cropped_avatar );
    14341460                } elseif ( 'group' === $avatar_data['object'] ) {
    14351461                        /** This action is documented in bp-groups/bp-groups-screens.php */
    1436                         do_action( 'groups_avatar_uploaded', (int) $avatar_data['item_id'], $avatar_data['type'], $r );
     1462                        do_action( 'groups_avatar_uploaded', (int) $avatar_data['item_id'], $avatar_data['type'], $r, $cropped_avatar );
    14371463                }
    14381464
    14391465                wp_send_json_success( $return );
    function bp_avatar_template_check() { 
    21222148                bp_attachments_get_template_part( 'avatars/index' );
    21232149        }
    21242150}
     2151
     2152/**
     2153 * Get a specific version of an avatar from its history
     2154 *
     2155 * @since 10.0.0
     2156 *
     2157 * @param int        $user_id   The user ID we need the avatar version for.
     2158 * @param int|string $timestamp An integer Unix timestamp or a date string of the format 'Y-m-d h:i:s'.
     2159 * @param string     $type      The type of avatar we need. Possible values are `thumb` and `full`.
     2160 * @return array                A list of matching results, an empty array if no avatars were found.
     2161 */
     2162function bp_avatar_get_version_src( $user_id = 0, $timestamp = '', $type = 'full' ) {
     2163        if ( ! $user_id || ! $timestamp ) {
     2164                return array();
     2165        }
     2166
     2167        // The avatar ID is the avatar file name without dot extension.
     2168        $avatar_id = '';
     2169
     2170        if ( ! is_numeric( $timestamp ) ) {
     2171                $timestamp = strtotime( $timestamp );
     2172        }
     2173
     2174        $avatar_id = $timestamp . '-bpfull';
     2175        if ( 'full' !== $type ) {
     2176                $avatar_id = $timestamp . '-bpthumb';
     2177        }
     2178
     2179        // The user avatar directory we are looking into to get the avatar url.
     2180        $user_avatar_dir = trailingslashit( bp_core_avatar_upload_path() ) . 'avatars/' . $user_id;
     2181
     2182        return bp_attachments_list_directory_files_recursively( $user_avatar_dir, $avatar_id );
     2183}
  • src/bp-core/classes/class-bp-attachment-avatar.php

    diff --git src/bp-core/classes/class-bp-attachment-avatar.php src/bp-core/classes/class-bp-attachment-avatar.php
    index d95ad4c4c..f13188bdd 100644
    class BP_Attachment_Avatar extends BP_Attachment { 
    199199         * @see  BP_Attachment::crop for the list of parameters
    200200         *
    201201         * @param array $args Array of arguments for the cropping.
    202          * @return array The cropped avatars (full and thumb).
     202         * @return array The cropped avatars (full, thumb and the timestamp).
    203203         */
    204204        public function crop( $args = array() ) {
    205205                // Bail if the original file is missing.
    class BP_Attachment_Avatar extends BP_Attachment { 
    255255
    256256                /**
    257257                 * Check that the new avatar doesn't have the same name as the
    258                  * old one before deleting
     258                 * old one before moving the previous one into history.
    259259                 */
    260260                if ( ! empty( $existing_avatar ) && $existing_avatar !== $this->url . $relative_path ) {
    261                         bp_core_delete_existing_avatar( array( 'object' => $args['object'], 'item_id' => $args['item_id'], 'avatar_path' => $avatar_folder_dir ) );
     261                        // Add a new revision for the existing avatar.
     262                        $avatars = bp_attachments_list_directory_files( $avatar_folder_dir );
     263
     264                        if ( $avatars ) {
     265                                foreach ( $avatars as $avatar_file ) {
     266                                        if ( ! isset( $avatar_file->name, $avatar_file->id, $avatar_file->path ) ) {
     267                                                continue;
     268                                        }
     269
     270                                        $is_full  = preg_match( "/-bpfull/", $avatar_file->name );
     271                                        $is_thumb = preg_match( "/-bpthumb/", $avatar_file->name );
     272
     273                                        if ( $is_full || $is_thumb ) {
     274                                                $revision = $this->add_revision(
     275                                                        'avatar',
     276                                                        array(
     277                                                                'file_abspath' => $avatar_file->path,
     278                                                                'file_id'      => $avatar_file->id,
     279                                                        )
     280                                                );
     281
     282                                                if ( is_wp_error( $revision ) ) {
     283                                                        error_log( $revision->get_error_message() );
     284                                                }
     285                                        }
     286                                }
     287                        }
    262288                }
    263289
    264290                // Make sure we at least have minimal data for cropping.
    class BP_Attachment_Avatar extends BP_Attachment { 
    272298
    273299                // Get the file extension.
    274300                $data = @getimagesize( $absolute_path );
    275                 $ext  = $data['mime'] == 'image/png' ? 'png' : 'jpg';
     301                $ext  = $data['mime'] === 'image/png' ? 'png' : 'jpg';
    276302
    277303                $args['original_file'] = $absolute_path;
    278304                $args['src_abs']       = false;
    279                 $avatar_types = array( 'full' => '', 'thumb' => '' );
     305
     306                $avatar_types = array(
     307                        'full'  => '',
     308                        'thumb' => '',
     309                );
     310                $timestamp   = bp_core_current_time( false, 'timestamp' );
    280311
    281312                foreach ( $avatar_types as $key_type => $type ) {
    282313                        if ( 'thumb' === $key_type ) {
    class BP_Attachment_Avatar extends BP_Attachment { 
    287318                                $args['dst_h'] = bp_core_avatar_full_height();
    288319                        }
    289320
    290                         $filename         = wp_unique_filename( $avatar_folder_dir, uniqid() . "-bp{$key_type}.{$ext}" );
     321                        $filename         = wp_unique_filename( $avatar_folder_dir, $timestamp . "-bp{$key_type}.{$ext}" );
    291322                        $args['dst_file'] = $avatar_folder_dir . '/' . $filename;
    292323
    293324                        $avatar_types[ $key_type ] = parent::crop( $args );
    class BP_Attachment_Avatar extends BP_Attachment { 
    296327                // Remove the original.
    297328                @unlink( $absolute_path );
    298329
    299                 // Return the full and thumb cropped avatars.
    300                 return $avatar_types;
     330                // Return the full, thumb cropped avatars and the timestamp.
     331                return array_merge(
     332                        $avatar_types,
     333                        array(
     334                                'timestamp' => $timestamp,
     335                        )
     336                );
    301337        }
    302338
    303339        /**
  • src/bp-core/classes/class-bp-attachment.php

    diff --git src/bp-core/classes/class-bp-attachment.php src/bp-core/classes/class-bp-attachment.php
    index 9362a6c12..248c44946 100644
    abstract class BP_Attachment { 
    559559                return $script_data;
    560560        }
    561561
     562        /**
     563         * Adds a new revision of a file.
     564         *
     565         * @since 10.0.0
     566         *
     567         * @param string $attachment_type The attachement type (eg: avatar).
     568         * @param array $args {
     569         *     @type string $file_abspath The source file (absolute path) for the attachment.
     570         *     @type string $file_id      Optional. The file ID to use as a suffix for the revision directory.
     571         * }
     572         * @return object|WP_Error An object informing about the URL an Path to a revision file, a WP_Error object on failure.
     573         */
     574        public function add_revision( $attachment_type, $args = array() ) {
     575                $r = bp_parse_args(
     576                        $args,
     577                        array(
     578                                'file_abspath' => '',
     579                                'file_id'      => '',
     580                        ),
     581                        'attachment_' . $attachment_type . '_add_revision'
     582                );
     583
     584                if ( ! $r['file_abspath'] ) {
     585                        return new WP_Error( 'missing_parameter', __( 'The absolute path to your file is missing.', 'buddypress' ) );
     586
     587                        // Make sure it's coming from an uploaded file.
     588                } elseif ( false === strpos( $r['file_abspath'], $this->upload_path ) ) {
     589                        return new WP_Error( 'forbidden_path', __( 'The absolute path to your file is not allowed.', 'buddypress' ) );
     590
     591                } else {
     592                        $filepath = $r['file_abspath'];
     593                }
     594
     595                $dirname  = trailingslashit( dirname( $filepath ) );
     596                $filename = sanitize_file_name( wp_basename( $filepath ) );
     597
     598                if ( ! $r['file_id'] ) {
     599                        $r['file_id'] = $filename;
     600                }
     601
     602                $file_id = wp_hash( $r['file_id'] );
     603
     604                // Set the revision name & dir.
     605                $revision_name = '';
     606                $revision_dir  = $dirname . '._revisions_' . $file_id;
     607
     608                // Avatars and Cover Images are specific attachments.
     609                if ( 'avatar' === $attachment_type || 'cover_image' === $attachment_type ) {
     610                        $revision_dir  = $dirname . 'history';
     611                }
     612
     613                // Create the revision directory if it doesn't exist yet.
     614                if ( ! is_dir( $revision_dir ) ) {
     615                        mkdir( $revision_dir );
     616                }
     617
     618                $revision_name = wp_unique_filename( $revision_dir, $filename );
     619                $revision_path = trailingslashit( $revision_dir ) . $revision_name;
     620
     621                if ( ! rename( $filepath, $revision_path ) ) {
     622                        return new WP_Error( 'missing_parameter', __( 'An unexpected error occured while adding the revision.', 'buddypress' ) );
     623                }
     624
     625                return (object) array(
     626                        'url'  => str_replace( trailingslashit( $this->upload_path ), trailingslashit( $this->url ), $revision_path ),
     627                        'path' => $revision_path,
     628                );
     629        }
     630
    562631        /**
    563632         * Get full data for an image
    564633         *
  • src/bp-groups/actions/create.php

    diff --git src/bp-groups/actions/create.php src/bp-groups/actions/create.php
    index 132429bd1..54e2cc589 100644
    function groups_action_create_group() { 
    270270                                'crop_h'        => $_POST['h']
    271271                        );
    272272
    273                         if ( ! bp_core_avatar_handle_crop( $args ) ) {
     273                        $cropped_avatar = bp_core_avatar_handle_crop( $args, 'array' );
     274
     275                        if ( ! $cropped_avatar ) {
    274276                                bp_core_add_message( __( 'There was an error saving the group profile photo, please try uploading again.', 'buddypress' ), 'error' );
    275277                        } else {
    276278                                /**
    277279                                 * Fires after a group avatar is uploaded.
    278280                                 *
    279281                                 * @since 2.8.0
     282                                 * @since 10.0.0 Adds a new param: an array containing the full, thumb avatar and the timestamp.
    280283                                 *
    281                                  * @param int    $group_id ID of the group.
    282                                  * @param string $type     Avatar type. 'crop' or 'full'.
    283                                  * @param array  $args     Array of parameters passed to the avatar handler.
     284                                 * @param int    $group_id       ID of the group.
     285                                 * @param string $type           Avatar type. 'crop' or 'camera'.
     286                                 * @param array  $args           Array of parameters passed to the crop handler.
     287                                 * @param array  $cropped_avatar Array containing the full, thumb avatar and the timestamp.
    284288                                 */
    285                                 do_action( 'groups_avatar_uploaded', bp_get_current_group_id(), 'crop', $args );
     289                                do_action( 'groups_avatar_uploaded', bp_get_current_group_id(), 'crop', $args, $cropped_avatar );
    286290
    287291                                bp_core_add_message( __( 'The group profile photo was uploaded successfully.', 'buddypress' ) );
    288292                        }
  • src/bp-groups/screens/single/admin/group-avatar.php

    diff --git src/bp-groups/screens/single/admin/group-avatar.php src/bp-groups/screens/single/admin/group-avatar.php
    index 3e2927a1e..57fc59451 100644
    function groups_screen_group_admin_avatar() { 
    7474                        'crop_h'        => $_POST['h']
    7575                );
    7676
    77                 if ( !bp_core_avatar_handle_crop( $args ) ) {
     77                $cropped_avatar = bp_core_avatar_handle_crop( $args, 'array' );
     78
     79                if ( ! $cropped_avatar ) {
    7880                        bp_core_add_message( __( 'There was a problem cropping the group profile photo.', 'buddypress' ), 'error' );
    7981                } else {
    8082                        /**
    8183                         * Fires after a group avatar is uploaded.
    8284                         *
    8385                         * @since 2.8.0
     86                         * @since 10.0.0 Adds a new param: an array containing the full, thumb avatar and the timestamp.
    8487                         *
    85                          * @param int    $group_id ID of the group.
    86                          * @param string $type     Avatar type. 'crop' or 'full'.
    87                          * @param array  $args     Array of parameters passed to the avatar handler.
     88                         * @param int    $group_id       ID of the group.
     89                         * @param string $type           Avatar type. 'crop' or 'camera'.
     90                         * @param array  $args           Array of parameters passed to the avatar handler.
     91                         * @param array  $cropped_avatar Array containing the full, thumb avatar and the timestamp.
    8892                         */
    89                         do_action( 'groups_avatar_uploaded', bp_get_current_group_id(), 'crop', $args );
     93                        do_action( 'groups_avatar_uploaded', bp_get_current_group_id(), 'crop', $args, $cropped_avatar );
    9094                        bp_core_add_message( __( 'The new group profile photo was uploaded successfully.', 'buddypress' ) );
    9195                }
    9296        }
    function groups_screen_group_admin_avatar() { 
    109113         */
    110114        bp_core_load_template( apply_filters( 'groups_template_group_admin_avatar', 'groups/single/home' ) );
    111115}
    112 add_action( 'bp_screens', 'groups_screen_group_admin_avatar' );
    113  No newline at end of file
     116add_action( 'bp_screens', 'groups_screen_group_admin_avatar' );
  • src/bp-members/bp-members-activity.php

    diff --git src/bp-members/bp-members-activity.php src/bp-members/bp-members-activity.php
    index 890ff2317..1645c1542 100644
    add_action( 'bp_core_activated_user', 'bp_core_new_user_activity' ); 
    166166 * Adds an activity stream item when a user has uploaded a new avatar.
    167167 *
    168168 * @since 8.0.0
     169 * @since 10.0.0 Adds the `$type`, `$crop_data` and `$cropped_avatar` parameters.
    169170 *
    170  * @param int $user_id The user id the avatar was set for.
     171 * @param int    $user_id        The user id the avatar was set for.
     172 * @param string $type           The way the avatar was set ('camera' or `crop`).
     173 * @param array  $crop_data      Array of parameters passed to the crop handler.
     174 * @param array  $cropped_avatar Array containing the full, thumb avatar and the timestamp.
    171175 */
    172 function bp_members_new_avatar_activity( $user_id = 0 ) {
     176function bp_members_new_avatar_activity( $user_id = 0, $type = '', $crop_data = array(), $cropped_avatar = array() ) {
    173177
    174178        // Bail if activity component is not active.
    175179        if ( ! bp_is_active( 'activity' ) ) {
    function bp_members_new_avatar_activity( $user_id = 0 ) { 
    230234                }
    231235        }
    232236
     237        $recorded_time = '';
     238        if ( isset( $cropped_avatar['timestamp'] ) && $cropped_avatar['timestamp'] ) {
     239                $recorded_time = date( 'Y-m-d H:i:s', $cropped_avatar['timestamp'] );
     240        }
     241
    233242        // Add the activity.
    234         bp_activity_add(
     243        $activity_id = bp_activity_add(
    235244                array(
    236                         'user_id'   => $user_id,
    237                         'component' => $bp->members->id,
    238                         'type'      => 'new_avatar',
     245                        'user_id'       => $user_id,
     246                        'component'     => $bp->members->id,
     247                        'type'          => 'new_avatar',
     248                        'recorded_time' => $recorded_time,
    239249                )
    240250        );
    241251}
    242 add_action( 'bp_members_avatar_uploaded', 'bp_members_new_avatar_activity' );
     252add_action( 'bp_members_avatar_uploaded', 'bp_members_new_avatar_activity', 10, 4 );
  • src/bp-members/screens/change-avatar.php

    diff --git src/bp-members/screens/change-avatar.php src/bp-members/screens/change-avatar.php
    index 5c4bcf440..6a421ec77 100644
    function bp_members_screen_change_avatar() { 
    6161                        'crop_h'        => $_POST['h']
    6262                );
    6363
    64                 if ( ! bp_core_avatar_handle_crop( $args ) ) {
     64                // Handle crop.
     65                $cropped_avatar = bp_core_avatar_handle_crop( $r, 'array' );
     66
     67                if ( ! $cropped_avatar ) {
    6568                        bp_core_add_message( __( 'There was a problem cropping your profile photo.', 'buddypress' ), 'error' );
    6669                } else {
    6770
    function bp_members_screen_change_avatar() { 
    7275                         * Fires right before the redirect, after processing a new avatar.
    7376                         *
    7477                         * @since 6.0.0
     78                         * @since 10.0.0 Adds a new param: an array containing the full, thumb avatar and the timestamp.
    7579                         *
    76                          * @param string $item_id Inform about the user id the avatar was set for.
    77                          * @param string $value   Inform about the way the avatar was set ('crop').
     80                         * @param string $item_id        Inform about the user id the avatar was set for.
     81                         * @param string $type           Inform about the way the avatar was set ('camera').
     82                         * @param array  $args           Array of parameters passed to the crop handler.
     83                         * @param array  $cropped_avatar Array containing the full, thumb avatar and the timestamp.
    7884                         */
    79                         do_action( 'bp_members_avatar_uploaded', (int) $args['item_id'], 'crop' );
     85                        do_action( 'bp_members_avatar_uploaded', (int) $args['item_id'], 'crop', $args, $cropped_avatar );
    8086
    8187                        bp_core_add_message( __( 'Your new profile photo was uploaded successfully.', 'buddypress' ) );
    8288                        bp_core_redirect( bp_displayed_user_domain() );
  • new file tests/phpunit/testcases/core/attachments.php

    diff --git tests/phpunit/testcases/core/attachments.php tests/phpunit/testcases/core/attachments.php
    new file mode 100644
    index 000000000..4acbf496a
    - +  
     1<?php
     2
     3/**
     4 * @group core
     5 */
     6
     7class BP_Tests_Core_Attachments extends BP_UnitTestCase {
     8        /**
     9         * @group bp_attachments_list_directory_files_recursively
     10         */
     11        public function test_bp_attachments_list_directory_files_recursively() {
     12                $files = bp_attachments_list_directory_files_recursively( BP_TESTS_DIR . 'assets', 'index' );
     13
     14                $this->assertTrue( 1 === count( $files ) );
     15                $this->assertTrue( isset( $files['templates/index'] ) );
     16        }
     17}
  • tests/phpunit/testcases/core/class-bp-attachment.php

    diff --git tests/phpunit/testcases/core/class-bp-attachment.php tests/phpunit/testcases/core/class-bp-attachment.php
    index fae4e150d..6bb1c308b 100644
    class BP_Tests_BP_Attachment_TestCases extends BP_UnitTestCase { 
    406406                $this->clean_files( 'shrink' );
    407407        }
    408408
     409        /**
     410         * @group add_revision
     411         */
     412        public function test_bp_attachment_add_revision() {
     413                if ( false === _wp_image_editor_choose() || version_compare( phpversion(), '7.0' , '<' ) ) {
     414                        $this->markTestSkipped( 'This test requires PHP >= 7.0 and to have a valid image editor that is compatible with WordPress.' );
     415                }
     416
     417                $image = BP_TESTS_DIR . 'assets/upside-down.jpg';
     418
     419                $attachment = new BPTest_Attachment_Extension(
     420                        array(
     421                                'base_dir'   => 'add_revision',
     422                                'action'     => 'attachment_action',
     423                                'file_input' => 'attachment_file_input',
     424                        )
     425                );
     426
     427                $abs_path_copy = $attachment->upload_path . '/upside-down.jpg';
     428                copy( $image, $abs_path_copy );
     429
     430                $revision = $attachment->add_revision(
     431                        'media',
     432                        array(
     433                                'file_abspath' => $abs_path_copy,
     434                                'file_id'      => 'media',
     435                        )
     436                );
     437
     438                $this->assertFalse( file_exists( $abs_path_copy ) );
     439                $this->assertTrue( file_exists( $revision->path ) );
     440
     441                // Cleanup
     442                @unlink( $revision->path );
     443                @rmdir( dirname( $revision->path ) );
     444                $this->clean_files( 'add_revision' );
     445        }
     446
     447        /**
     448         * @group add_revision
     449         * @group avatars
     450         */
     451        public function test_bp_attachment_add_avatar_history() {
     452                if ( false === _wp_image_editor_choose() || version_compare( phpversion(), '7.0' , '<' ) ) {
     453                        $this->markTestSkipped( 'This test requires PHP >= 7.0 and to have a valid image editor that is compatible with WordPress.' );
     454                }
     455
     456                $image = BP_TESTS_DIR . 'assets/upside-down.jpg';
     457
     458                $attachment = new BPTest_Attachment_Extension(
     459                        array(
     460                                'base_dir'   => 'add_history',
     461                                'action'     => 'attachment_action',
     462                                'file_input' => 'attachment_file_input',
     463                        )
     464                );
     465
     466                $abs_path_copy = $attachment->upload_path . '/upside-down.jpg';
     467                copy( $image, $abs_path_copy );
     468
     469                $revision = $attachment->add_revision(
     470                        'avatar',
     471                        array(
     472                                'file_abspath' => $abs_path_copy,
     473                                'file_id'      => 'avatar',
     474                        )
     475                );
     476
     477                $this->assertFalse( file_exists( $abs_path_copy ) );
     478                $this->assertTrue( file_exists( $revision->path ) );
     479                $this->assertSame( $attachment->url . '/history/upside-down.jpg', $revision->url );
     480
     481                // Cleanup
     482                @unlink( $revision->path );
     483                @rmdir( dirname( $revision->path ) );
     484                $this->clean_files( 'add_history' );
     485        }
     486
    409487        public function limit_to_50px( $max_width ) {
    410488                return 50;
    411489        }