Skip to:
Content

BuddyPress.org

Ticket #6290: 6290.11.patch

File 6290.11.patch, 91.2 KB (added by imath, 5 years ago)
  • src/bp-core/bp-core-attachments.php

    diff --git src/bp-core/bp-core-attachments.php src/bp-core/bp-core-attachments.php
    index e69de29..7df95a6 100644
     
     1<?php
     2/**
     3 * BuddyPress Attachments functions.
     4 *
     5 * @package BuddyPress
     6 * @subpackage Attachments
     7 */
     8
     9// Exit if accessed directly
     10defined( 'ABSPATH' ) || exit;
     11
     12/**
     13 * Get the BuddyPress Plupload settings
     14 *
     15 * @since  BuddyPress (2.3.0)
     16 *
     17 * @return array list of BuddyPress Plupload settings
     18 */
     19function bp_attachments_get_plupload_default_settings() {
     20
     21        $max_upload_size = wp_max_upload_size();
     22
     23        if ( ! $max_upload_size ) {
     24                $max_upload_size = 0;
     25        }
     26
     27        $defaults = array(
     28                'runtimes'            => 'html5,flash,silverlight,html4',
     29                'file_data_name'      => 'file',
     30                'multipart_params'    => array(
     31                        'action'          => 'bp_upload_attachment',
     32                        '_wpnonce'        => wp_create_nonce( 'bp-uploader' ),
     33                ),
     34                'url'                 => admin_url( 'admin-ajax.php', 'relative' ),
     35                'flash_swf_url'       => includes_url( 'js/plupload/plupload.flash.swf' ),
     36                'silverlight_xap_url' => includes_url( 'js/plupload/plupload.silverlight.xap' ),
     37                'filters' => array(
     38                        'max_file_size'   => $max_upload_size . 'b',
     39                ),
     40                'multipart'           => true,
     41                'urlstream_upload'    => true,
     42        );
     43
     44        // WordPress is not allowing multi selection for iOs 7 device.. See #29602.
     45        if ( wp_is_mobile() && strpos( $_SERVER['HTTP_USER_AGENT'], 'OS 7_' ) !== false &&
     46                strpos( $_SERVER['HTTP_USER_AGENT'], 'like Mac OS X' ) !== false ) {
     47
     48                $defaults['multi_selection'] = false;
     49        }
     50
     51        $settings = array(
     52                'defaults' => $defaults,
     53                'browser'  => array(
     54                        'mobile'    => wp_is_mobile(),
     55                        'supported' => _device_can_upload(),
     56                ),
     57                'limitExceeded' => is_multisite() && ! is_upload_space_available(),
     58        );
     59
     60        /**
     61         * Filter the BuddyPress Plupload default settings.
     62         *
     63         * @since 2.3.0
     64         *
     65         * @param array $params Default Plupload parameters array.
     66         */
     67        return apply_filters( 'bp_attachments_get_plupload_default_settings', $settings );
     68}
     69
     70/**
     71 * Builds localization strings for the BuddyPress Uploader scripts
     72 *
     73 * @since  BuddyPress (2.3.0)
     74 *
     75 * @return array Plupload default localization strings
     76 */
     77function bp_attachments_get_plupload_l10n() {
     78        // Localization strings
     79        return apply_filters( 'bp_attachments_get_plupload_l10n', array(
     80                        'queue_limit_exceeded'      => __( 'You have attempted to queue too many files.', 'buddypress' ),
     81                        'file_exceeds_size_limit'   => __( '%s exceeds the maximum upload size for this site.', 'buddypress' ),
     82                        'zero_byte_file'            => __( 'This file is empty. Please try another.', 'buddypress' ),
     83                        'invalid_filetype'          => __( 'This file type is not allowed. Please try another.', 'buddypress' ),
     84                        'not_an_image'              => __( 'This file is not an image. Please try another.', 'buddypress' ),
     85                        'image_memory_exceeded'     => __( 'Memory exceeded. Please try another smaller file.', 'buddypress' ),
     86                        'image_dimensions_exceeded' => __( 'This is larger than the maximum size. Please try another.', 'buddypress' ),
     87                        'default_error'             => __( 'An error occurred. Please try again later.', 'buddypress' ),
     88                        'missing_upload_url'        => __( 'There was a configuration error. Please contact the server administrator.', 'buddypress' ),
     89                        'upload_limit_exceeded'     => __( 'You may only upload 1 file.', 'buddypress' ),
     90                        'http_error'                => __( 'HTTP error.', 'buddypress' ),
     91                        'upload_failed'             => __( 'Upload failed.', 'buddypress' ),
     92                        'big_upload_failed'         => __( 'Please try uploading this file with the %1$sbrowser uploader%2$s.', 'buddypress' ),
     93                        'big_upload_queued'         => __( '%s exceeds the maximum upload size for the multi-file uploader when used in your browser.', 'buddypress' ),
     94                        'io_error'                  => __( 'IO error.', 'buddypress' ),
     95                        'security_error'            => __( 'Security error.', 'buddypress' ),
     96                        'file_cancelled'            => __( 'File canceled.', 'buddypress' ),
     97                        'upload_stopped'            => __( 'Upload stopped.', 'buddypress' ),
     98                        'dismiss'                   => __( 'Dismiss', 'buddypress' ),
     99                        'crunching'                 => __( 'Crunching&hellip;', 'buddypress' ),
     100                        'unique_file_warning'       => __( 'Make sure to upload a unique file', 'buddypress' ),
     101                        'error_uploading'           => __( '&#8220;%s&#8221; has failed to upload.', 'buddypress' )
     102        ) );
     103}
     104
     105/**
     106 * Enqueues the script needed for the Uploader UI
     107 *
     108 * @see  BP_Attachment::script_data() && BP_Attachment_Avatar::script_data() for examples showing how
     109 * to set specific script data
     110 *
     111 * @since  BuddyPress (2.3.0)
     112 *
     113 * @param  string $class name of the class extending BP_Attachment (eg: BP_Attachment_Avatar)
     114 */
     115function bp_attachments_enqueue_scripts( $class = '' ) {
     116        // Enqueue me just once per page, please.
     117        if ( did_action( 'bp_attachments_enqueue_scripts' ) ) {
     118                return;
     119        }
     120
     121        if ( ! $class || ! class_exists( $class ) ) {
     122                return new WP_Error( 'missing_parameter' );
     123        }
     124
     125        // Get an instance of the class and get the script data
     126        $attachment = new $class;
     127        $script_data  = $attachment->script_data();
     128
     129        $args = bp_parse_args( $script_data, array(
     130                'action'            => '',
     131                'file_data_name'    => '',
     132                'max_file_size'     => 0,
     133                'browse_button'     => 'bp-browse-button',
     134                'container'         => 'bp-upload-ui',
     135                'drop_element'      => 'drag-drop-area',
     136                'bp_params'         => array(),
     137                'extra_css'         => array(),
     138                'extra_js'          => array(),
     139                'feedback_messages' => array(),
     140        ), 'attachments_enqueue_scripts' );
     141
     142        if ( empty( $args['action'] ) || empty( $args['file_data_name'] ) ) {
     143                return new WP_Error( 'missing_parameter' );
     144        }
     145
     146        // Get the BuddyPress uploader strings
     147        $strings = bp_attachments_get_plupload_l10n();
     148
     149        // Get the BuddyPress uploader settings
     150        $settings = bp_attachments_get_plupload_default_settings();
     151
     152        // Set feedback messages
     153        if ( ! empty( $args['feedback_messages'] ) ) {
     154                $strings['feedback_messages'] = $args['feedback_messages'];
     155        }
     156
     157        // Use a temporary var to ease manipulation
     158        $defaults = $settings['defaults'];
     159
     160        // Set the upload action
     161        $defaults['multipart_params']['action'] = $args['action'];
     162
     163        // Set BuddyPress upload parameters if provided
     164        if ( ! empty( $args['bp_params'] ) ) {
     165                $defaults['multipart_params']['bp_params'] = $args['bp_params'];
     166        }
     167
     168        // Merge other arguments
     169        $ui_args = array_intersect_key( $args, array(
     170                'file_data_name' => true,
     171                'browse_button'  => true,
     172                'container'      => true,
     173                'drop_element'   => true,
     174        ) );
     175
     176        $defaults = array_merge( $defaults, $ui_args );
     177
     178        if ( ! empty( $args['max_file_size'] ) ) {
     179                $defaults['filters']['max_file_size'] = $args['max_file_size'] . 'b';
     180        }
     181
     182        // Specific to BuddyPress Avatars
     183        if ( 'bp_avatar_upload' === $defaults['multipart_params']['action'] ) {
     184
     185                // Include the cropping informations for avatars
     186                $settings['crop'] = array(
     187                        'full_h'  => bp_core_avatar_full_height(),
     188                        'full_w'  => bp_core_avatar_full_width(),
     189                );
     190
     191                // Avatar only need 1 file and 1 only!
     192                $defaults['multi_selection'] = false;
     193
     194                // Does the object already has an avatar set
     195                $has_avatar = $defaults['multipart_params']['bp_params']['has_avatar'];
     196
     197                // What is the object the avatar belongs to
     198                $object = $defaults['multipart_params']['bp_params']['object'];
     199
     200                // Init the Avatar nav
     201                $avatar_nav = array(
     202                        'upload' => array( 'id' => 'upload', 'caption' => __( 'Upload', 'buddypress' ), 'order' => 0  ),
     203
     204                        // The delete view will only show if the object has an avatar
     205                        'delete' => array( 'id' => 'delete', 'caption' => __( 'Delete', 'buddypress' ), 'order' => 100, 'hide' => (int) ! $has_avatar ),
     206                );
     207
     208                // Use this filter to disable the Webcam Avatar feature
     209                if ( false !== apply_filters( 'bp_attachment_avatar_use_webcam', true ) && 'user' === $object ) {
     210                        $avatar_nav['camera'] = array( 'id' => 'camera', 'caption' => __( 'Camera', 'buddypress' ), 'order' => 10 );
     211
     212                        // Set warning messages
     213                        $strings['camera_warnings'] = array(
     214                                'requesting'  => __( 'Requesting video stream, please authorize this website to access to your camera.', 'buddypress'),
     215                                'loading'     => __( 'Please wait for the video to load.', 'buddypress' ),
     216                                'loaded'      => __( 'Video stream loaded. You can use the capture button to display the profile photo preview.', 'buddypress' ),
     217                                'noaccess'    => __( 'You denied this website to access to your camera. Please use the upload form.', 'buddypress' ),
     218                                'errormsg'    => __( 'Your browser is not supported. Please use the upload form.', 'buddypress' ),
     219                                'videoerror'  => __( 'Video error. Please use the upload form.', 'buddypress' ),
     220                                'ready'       => __( 'Your profile photo is ready, use the save button to validate.', 'buddypress' ),
     221                                'nocapture'   => __( 'No profile photo was captured, please use the capture button first.', 'buddypress' ),
     222                        );
     223                }
     224
     225                /**
     226                 * Use this filter to add a navigation to a custom tool to set the object's avatar
     227                 *
     228                 * @since BuddyPress (2.3.0)
     229                 *
     230                 * @param array $avatar_nav An associative array of available nav items where each item is an array organized this way:
     231                 * $avatar_nav[ $nav_item_id ] {
     232                 *     @type string $nav_item_id the nav item id in lower case without special characters or space
     233                 *     @type string $caption     the name of the item nav that will be displayed in the nav
     234                 *     @type int    $order       An integer to specify the priority of the item nav, choose one
     235                 *                               between 1 and 99 to be after the uploader nav item and before the delete nav item
     236                 *     @type int    $hide        if set to 1 the item nav will be hidden
     237                 *                               (only used for the delete nav item)
     238                 * }
     239                 * @param string $object the object the avatar belongs to (eg: user or group)
     240                 */
     241                $settings['nav'] = bp_sort_by_key( apply_filters( 'bp_attachments_avatar_nav', $avatar_nav, $object ), 'order', 'num' );
     242        }
     243
     244        // Set Plupload settings
     245        $settings['defaults'] = $defaults;
     246
     247        /**
     248         * Enqueue some extra styles if required
     249         *
     250         * Extra styles need to be registered.
     251         */
     252        if ( ! empty( $args['extra_css'] ) ) {
     253                foreach ( (array) $args['extra_css'] as $css ) {
     254                        if ( empty( $css ) ) {
     255                                continue;
     256                        }
     257
     258                        wp_enqueue_style( $css );
     259                }
     260        }
     261
     262        wp_enqueue_script ( 'bp-plupload' );
     263        wp_localize_script( 'bp-plupload', 'BP_Uploader', array( 'strings' => $strings, 'settings' => $settings ) );
     264
     265        /**
     266         * Enqueue some extra scripts if required
     267         *
     268         * Extra scripts need to be registered.
     269         */
     270        if ( ! empty( $args['extra_js'] ) ) {
     271                foreach ( (array) $args['extra_js'] as $js ) {
     272                        if ( empty( $js ) ) {
     273                                continue;
     274                        }
     275
     276                        wp_enqueue_script( $js );
     277                }
     278        }
     279
     280        /**
     281         * Fires at the conclusion of bp_attachments_enqueue_scripts()
     282         * to avoid the scripts to be loaded more than once.
     283         *
     284         * @since BuddyPress 2.3.0
     285         */
     286        do_action( 'bp_attachments_enqueue_scripts' );
     287}
     288
     289/**
     290 * Check the current user's capability to edit an avatar for a given object
     291 *
     292 * @since  BuddyPress (2.3.0)
     293 *
     294 * @param  string $capability the capability to check
     295 * @param  array  $args an array containing the item_id and the object to check
     296 */
     297function bp_attachments_current_user_can( $capability, $args = array() ) {
     298        $can = false;
     299
     300        if ( 'edit_avatar' === $capability ) {
     301                /**
     302                 * Needed avatar arguments are set.
     303                 */
     304                if ( isset( $args['item_id'] ) && isset( $args['object'] ) ) {
     305                        // Group profile photo
     306                        if ( bp_is_active( 'groups' ) && 'group' === $args['object'] ) {
     307                                if ( bp_is_group_create() ) {
     308                                        $can = (bool) groups_is_user_creator( bp_loggedin_user_id(), $args['item_id'] ) || bp_current_user_can( 'bp_moderate' );
     309                                } else {
     310                                        $can = (bool) groups_is_user_admin( bp_loggedin_user_id(), $args['item_id'] ) || bp_current_user_can( 'bp_moderate' );
     311                                }
     312                        // User profile photo
     313                        } elseif ( bp_is_active( 'xprofile' ) && 'user' === $args['object'] ) {
     314                                $can = bp_loggedin_user_id() === (int) $args['item_id'] || bp_current_user_can( 'bp_moderate' );
     315                        }
     316                /**
     317                 * No avatar arguments, fallback to bp_user_can_create_groups()
     318                 * or bp_is_item_admin()
     319                 */
     320                } else {
     321                        if ( bp_is_group_create() ) {
     322                                $can = bp_user_can_create_groups();
     323                        } else {
     324                                $can = bp_is_item_admin();
     325                        }
     326                }
     327        }
     328
     329        return apply_filters( 'bp_attachments_current_user_can', $can, $capability, $args );
     330}
     331
     332/**
     333 * Send a JSON response back to an Ajax upload request.
     334 *
     335 * @since  BuddyPress (2.3.0)
     336 *
     337 * @param  bool true for a success, false otherwise
     338 * @param  bool true if the Plupload runtime used is html4, false otherwise.
     339 * @param  mixed $data Data to encode as JSON, then print and die.
     340 */
     341function bp_attachments_json_response( $success, $is_html4 = false, $data = null ) {
     342        $response = array( 'success' => $success );
     343
     344        if ( isset( $data ) ) {
     345                $response['data'] = $data;
     346        }
     347
     348        // Send regular json response
     349        if ( ! $is_html4 ) {
     350                wp_send_json( $response );
     351
     352        /**
     353         * Send specific json response
     354         * the html4 Plupload handler requires a text/html content-type for older IE.
     355         * See https://core.trac.wordpress.org/ticket/31037
     356         */
     357        } else {
     358                echo wp_json_encode( $response );
     359
     360                wp_die();
     361        }
     362}
     363
     364/**
     365 * Get an Attachment template part.
     366 *
     367 * @since  BuddyPress (2.3.0)
     368 *
     369 * @param  string Template part slug. eg 'uploader' for 'uploader.php'.
     370 */
     371function bp_attachments_get_template_part( $slug ) {
     372        $attachment_template_part = 'assets/_attachments/' . $slug;
     373
     374        // Load the attachment template in WP Administratin screens
     375        if ( is_admin() && ( ! defined( 'DOING_AJAX' ) || ! DOING_AJAX ) ) {
     376                $attachment_admin_template_part = buddypress()->themes_dir . '/bp-legacy/buddypress/' . $attachment_template_part . '.php';
     377
     378                // Check the template part exists
     379                if ( ! file_exists( $attachment_admin_template_part ) ) {
     380                        return false;
     381                }
     382
     383                // load the template part
     384                require( $attachment_admin_template_part );
     385
     386        // Load the attachment template in WP_USE_THEMES env.
     387        } else {
     388                bp_get_template_part( $attachment_template_part );
     389        }
     390}
  • src/bp-core/bp-core-avatars.php

    diff --git src/bp-core/bp-core-avatars.php src/bp-core/bp-core-avatars.php
    index 8c6ca3a..1bd919f 100644
    function bp_core_delete_existing_avatar( $args = '' ) { 
    554554}
    555555
    556556/**
     557 * Ajax delete an avatar for a given object and item id
     558 *
     559 * @since  BuddyPress (2.3.0)
     560 *
     561 * @return  string a json object containing success data if the avatar was deleted
     562 *                 error message otherwise
     563 */
     564function bp_avatar_ajax_delete() {
     565        // Bail if not a POST action
     566        if ( 'POST' !== strtoupper( $_SERVER['REQUEST_METHOD'] ) ) {
     567                wp_send_json_error();
     568        }
     569
     570        $avatar_data = $_POST;
     571
     572        if ( empty( $avatar_data['object'] ) || empty( $avatar_data['item_id'] ) ) {
     573                wp_send_json_error();
     574        }
     575
     576        $nonce = 'bp_delete_avatar_link';
     577        if ( 'group' === $avatar_data['object'] ) {
     578                $nonce = 'bp_group_avatar_delete';
     579        }
     580
     581        // Check the nonce
     582        check_admin_referer( $nonce, 'nonce' );
     583
     584        // Capability check
     585        if ( ! bp_attachments_current_user_can( 'edit_avatar', $avatar_data ) ) {
     586                wp_send_json_error();
     587        }
     588
     589        // Handle delete
     590        if ( bp_core_delete_existing_avatar( array( 'item_id' => $avatar_data['item_id'], 'object' => $avatar_data['object'] ) ) ) {
     591                $return = array(
     592                        'avatar' => html_entity_decode( bp_core_fetch_avatar( array(
     593                                'object'  => $avatar_data['object'],
     594                                'item_id' => $avatar_data['item_id'],
     595                                'html'    => false,
     596                                'type'    => 'full',
     597                        ) ) ),
     598                        'feedback_code' => 4,
     599                        'item_id'       => $avatar_data['item_id'],
     600                );
     601
     602                wp_send_json_success( $return );
     603        } else {
     604                wp_send_json_error( array(
     605                        'feedback_code' => 3,
     606                ) );
     607        }
     608}
     609add_action( 'wp_ajax_bp_avatar_delete', 'bp_avatar_ajax_delete' );
     610
     611/**
    557612 * Handle avatar uploading.
    558613 *
    559614 * The functions starts off by checking that the file has been uploaded
    function bp_core_avatar_handle_upload( $file, $upload_dir_filter ) { 
    626681}
    627682
    628683/**
     684 * Ajax upload an avatar
     685 *
     686 * @since BuddyPress (2.3.0)
     687 *
     688 * @return  string a json object containing success data if the upload succeeded
     689 *                 error message otherwise
     690 */
     691function bp_avatar_ajax_upload() {
     692        // Bail if not a POST action
     693        if ( 'POST' !== strtoupper( $_SERVER['REQUEST_METHOD'] ) ) {
     694                wp_die();
     695        }
     696
     697        /**
     698         * Sending the json response will be different if
     699         * the current Plupload runtime is html4
     700         */
     701        $is_html4 = false;
     702        if ( ! empty( $_POST['html4' ] ) ) {
     703                $is_html4 = true;
     704        }
     705
     706        // Check the nonce
     707        check_admin_referer( 'bp-uploader' );
     708
     709        // Init the BuddyPress parameters
     710        $bp_params = array();
     711
     712        // We need it to carry on
     713        if ( ! empty( $_POST['bp_params' ] ) ) {
     714                $bp_params = $_POST['bp_params' ];
     715        } else {
     716                bp_attachments_json_response( false, $is_html4 );
     717        }
     718
     719        // We need the object to set the uploads dir filter
     720        if ( empty( $bp_params['object'] ) ) {
     721                bp_attachments_json_response( false, $is_html4 );
     722        }
     723
     724        // Capability check
     725        if ( ! bp_attachments_current_user_can( 'edit_avatar', $bp_params ) ) {
     726                bp_attachments_json_response( false, $is_html4 );
     727        }
     728
     729        $bp = buddypress();
     730        $bp_params['upload_dir_filter'] = '';
     731        $needs_reset = array();
     732
     733        if ( 'user' === $bp_params['object'] && bp_is_active( 'xprofile' ) ) {
     734                $bp_params['upload_dir_filter'] = 'xprofile_avatar_upload_dir';
     735
     736                if ( ! bp_displayed_user_id() && ! empty( $bp_params['item_id'] ) ) {
     737                        $needs_reset = array( 'key' => 'displayed_user', 'value' => $bp->displayed_user );
     738                        $bp->displayed_user->id = $bp_params['item_id'];
     739                }
     740        } elseif ( 'group' === $bp_params['object'] && bp_is_active( 'groups' ) ) {
     741                $bp_params['upload_dir_filter'] = 'groups_avatar_upload_dir';
     742
     743                if ( ! bp_get_current_group_id() && ! empty( $bp_params['item_id'] ) ) {
     744                        $needs_reset = array( 'component' => 'groups', 'key' => 'current_group', 'value' => $bp->groups->current_group );
     745                        $bp->groups->current_group = groups_get_group( array(
     746                                'group_id'        => $bp_params['item_id'],
     747                                'populate_extras' => false,
     748                        ) );
     749                }
     750        } else {
     751                /**
     752                 * Filter here to deal with other components
     753                 *
     754                 * @since BuddyPress (2.3.0)
     755                 *
     756                 * @var array $bp_params the BuddyPress Ajax parameters
     757                 */
     758                $bp_params = apply_filters( 'bp_core_avatar_ajax_upload_params', $bp_params );
     759        }
     760
     761        if ( ! isset( $bp->avatar_admin ) ) {
     762                $bp->avatar_admin = new stdClass();
     763        }
     764
     765        // Upload the avatar
     766        $avatar = bp_core_avatar_handle_upload( $_FILES, $bp_params['upload_dir_filter'] );
     767
     768        // Reset objects
     769        if ( ! empty( $needs_reset ) ) {
     770                if ( ! empty( $needs_reset['component'] ) ) {
     771                        $bp->{$needs_reset['component']}->{$needs_reset['key']} = $needs_reset['value'];
     772                } else {
     773                        $bp->{$needs_reset['key']} = $needs_reset['value'];
     774                }
     775        }
     776
     777        if ( empty( $avatar ) ) {
     778                // Default upload error
     779                $message = array();
     780
     781                // Intercept the template message and remove it
     782                if ( ! empty( $bp->template_message ) ) {
     783                        // Set the feedback message
     784                        $message = array(
     785                                'type'    => 'upload_error',
     786                                'message' => $bp->template_message,
     787                        );
     788
     789                        // Remove template message.
     790                        $bp->template_message      = false;
     791                        $bp->template_message_type = false;
     792                        @setcookie( 'bp-message', false, time() - 1000, COOKIEPATH );
     793                        @setcookie( 'bp-message-type', false, time() - 1000, COOKIEPATH );
     794                }
     795
     796                bp_attachments_json_response( false, $is_html4, $message );
     797        }
     798
     799        if ( empty( $bp->avatar_admin->image->file ) ) {
     800                bp_attachments_json_response( false, $is_html4 );
     801        }
     802
     803        $uploaded_image = @getimagesize( $bp->avatar_admin->image->file );
     804
     805        // Set the name of the file
     806        $name = $_FILES['file']['name'];
     807        $name_parts = pathinfo( $name );
     808        $name = trim( substr( $name, 0, - ( 1 + strlen( $name_parts['extension'] ) ) ) );
     809
     810        if ( 'user' === $bp_params['object'] ) {
     811                do_action( 'xprofile_avatar_uploaded' );
     812        }
     813
     814        // Finally return the avatar to the editor
     815        bp_attachments_json_response( true, $is_html4, array(
     816                'name'      => $name,
     817                'url'       => $bp->avatar_admin->image->url,
     818                'width'     => $uploaded_image[0],
     819                'height'    => $uploaded_image[1],
     820        ) );
     821}
     822add_action( 'wp_ajax_bp_avatar_upload', 'bp_avatar_ajax_upload' );
     823
     824 /**
     825 * Handle avatar webcam capture.
     826 *
     827 * @since BuddyPress (2.3.0)
     828 *
     829 * @param string $data base64 encoded image.
     830 * @param int $item_id.
     831 * @return bool True on success, false on failure.
     832 */
     833function bp_avatar_handle_capture( $data = '', $item_id = 0 ) {
     834        if ( empty( $data ) || empty( $item_id ) ) {
     835                return false;
     836        }
     837
     838        $avatar_dir = bp_core_avatar_upload_path() . '/avatars';
     839
     840        // It's not a regular upload, we may need to create this folder
     841        if ( ! file_exists( $avatar_dir ) ) {
     842                if ( ! wp_mkdir_p( $avatar_dir ) ) {
     843                        return false;
     844                }
     845        }
     846
     847        $avatar_folder_dir = apply_filters( 'bp_core_avatar_folder_dir', $avatar_dir . '/' . $item_id, $item_id, 'user', 'avatars' );
     848
     849        // It's not a regular upload, we may need to create this folder
     850        if( ! is_dir( $avatar_folder_dir ) ) {
     851                if ( ! wp_mkdir_p( $avatar_folder_dir ) ) {
     852                        return false;
     853                }
     854        }
     855
     856        $original_file = $avatar_folder_dir . '/webcam-capture-' . $item_id . '.png';
     857
     858        if ( file_put_contents( $original_file, $data ) ) {
     859                $avatar_to_crop = str_replace( bp_core_avatar_upload_path(), '', $original_file );
     860
     861                // Crop to default values
     862                $crop_args = array( 'item_id' => $item_id, 'original_file' => $avatar_to_crop, 'crop_x' => 0, 'crop_y' => 0 );
     863
     864                do_action( 'xprofile_avatar_uploaded' );
     865
     866                return bp_core_avatar_handle_crop( $crop_args );
     867        } else {
     868                return false;
     869        }
     870}
     871
     872/**
    629873 * Crop an uploaded avatar.
    630874 *
    631875 * $args has the following parameters:
    function bp_core_avatar_handle_crop( $args = '' ) { 
    690934}
    691935
    692936/**
     937 * Ajax set an avatar for a given object and item id
     938 *
     939 * @since BuddyPress (2.3.0)
     940 *
     941 * @return  string a json object containing success data if the crop/capture succeeded
     942 *                 error message otherwise
     943 */
     944function bp_avatar_ajax_set() {
     945        // Bail if not a POST action
     946        if ( 'POST' !== strtoupper( $_SERVER['REQUEST_METHOD'] ) ) {
     947                wp_send_json_error();
     948        }
     949
     950        // Check the nonce
     951        check_admin_referer( 'bp_avatar_cropstore', 'nonce' );
     952
     953        $avatar_data = wp_parse_args( $_POST, array(
     954                'crop_w' => bp_core_avatar_full_width(),
     955                'crop_h' => bp_core_avatar_full_height(),
     956                'crop_x' => 0,
     957                'crop_y' => 0
     958        ) );
     959
     960        if ( empty( $avatar_data['object'] ) || empty( $avatar_data['item_id'] ) || empty( $avatar_data['original_file'] ) ) {
     961                wp_send_json_error();
     962        }
     963
     964        // Capability check
     965        if ( ! bp_attachments_current_user_can( 'edit_avatar', $avatar_data ) ) {
     966                wp_send_json_error();
     967        }
     968
     969        if ( ! empty( $avatar_data['type'] ) && 'camera' === $avatar_data['type'] && 'user' === $avatar_data['object'] ) {
     970                $webcam_avatar = false;
     971
     972                if ( ! empty( $avatar_data['original_file'] ) ) {
     973                        $webcam_avatar = str_replace( array( 'data:image/png;base64,', ' ' ), array( '', '+' ), $avatar_data['original_file'] );
     974                        $webcam_avatar = base64_decode( $webcam_avatar );
     975                }
     976
     977                if ( ! bp_avatar_handle_capture( $webcam_avatar, $avatar_data['item_id'] ) ) {
     978                        wp_send_json_error( array(
     979                                'feedback_code' => 1
     980                        ) );
     981
     982                } else {
     983                        $return = array(
     984                                'avatar' => html_entity_decode( bp_core_fetch_avatar( array(
     985                                        'object'  => $avatar_data['object'],
     986                                        'item_id' => $avatar_data['item_id'],
     987                                        'html'    => false,
     988                                        'type'    => 'full',
     989                                ) ) ),
     990                                'feedback_code' => 2,
     991                                'item_id'       => $avatar_data['item_id'],
     992                        );
     993
     994                        do_action( 'xprofile_screen_change_avatar' );
     995
     996                        wp_send_json_success( $return );
     997                }
     998
     999                return;
     1000        }
     1001
     1002        $original_file = str_replace( bp_core_avatar_url(), '', $avatar_data['original_file'] );
     1003
     1004        // Set avatars dir & feedback part
     1005        if ( 'user' === $avatar_data['object'] ) {
     1006                $avatar_dir = 'avatars';
     1007
     1008        // Defaults to object-avatars dir
     1009        } else {
     1010                $avatar_dir = sanitize_key( $avatar_data['object'] ) . '-avatars';
     1011        }
     1012
     1013        // Crop args
     1014        $r = array(
     1015                'item_id'       => $avatar_data['item_id'],
     1016                'object'        => $avatar_data['object'],
     1017                'avatar_dir'    => $avatar_dir,
     1018                'original_file' => $original_file,
     1019                'crop_w'        => $avatar_data['crop_w'],
     1020                'crop_h'        => $avatar_data['crop_h'],
     1021                'crop_x'        => $avatar_data['crop_x'],
     1022                'crop_y'        => $avatar_data['crop_y']
     1023        );
     1024
     1025        // Handle crop
     1026        if ( bp_core_avatar_handle_crop( $r ) ) {
     1027                $return = array(
     1028                        'avatar' => html_entity_decode( bp_core_fetch_avatar( array(
     1029                                'object'  => $avatar_data['object'],
     1030                                'item_id' => $avatar_data['item_id'],
     1031                                'html'    => false,
     1032                                'type'    => 'full',
     1033                        ) ) ),
     1034                        'feedback_code' => 2,
     1035                        'item_id'       => $avatar_data['item_id'],
     1036                );
     1037
     1038                if ( 'user' === $avatar_data['object'] ) {
     1039                        do_action( 'xprofile_screen_change_avatar' );
     1040                }
     1041
     1042                wp_send_json_success( $return );
     1043        } else {
     1044                wp_send_json_error( array(
     1045                        'feedback_code' => 1,
     1046                ) );
     1047        }
     1048}
     1049add_action( 'wp_ajax_bp_avatar_set', 'bp_avatar_ajax_set' );
     1050
     1051/**
    6931052 * Replace default WordPress avatars with BP avatars, if available.
    6941053 *
    6951054 * Filters 'get_avatar'.
    function bp_core_avatar_reset_query( $posts_query = null ) { 
    10991458        }
    11001459}
    11011460add_action( 'bp_parse_query', 'bp_core_avatar_reset_query', 10, 1 );
     1461
     1462/**
     1463 * Checks whether Avatar UI should be loaded
     1464 *
     1465 * @since  BuddyPress (2.3.0)
     1466 *
     1467 * @return bool True if Avatar UI should load, false otherwise
     1468 */
     1469function bp_avatar_is_front_edit() {
     1470        $retval = false;
     1471
     1472        if ( bp_is_user_change_avatar() && 'crop-image' !== bp_get_avatar_admin_step() ) {
     1473                $retval = true;
     1474        }
     1475
     1476        if ( bp_is_active( 'groups' ) ) {
     1477                // Group creation
     1478                if ( bp_is_group_create() && bp_is_group_creation_step( 'group-avatar' ) && 'crop-image' !== bp_get_avatar_admin_step() ) {
     1479                        $retval = true;
     1480
     1481                // Group Manage
     1482                } elseif ( bp_is_group_admin_page() && bp_is_group_admin_screen( 'group-avatar' ) && 'crop-image' !== bp_get_avatar_admin_step() ) {
     1483                        $retval = true;
     1484                }
     1485        }
     1486
     1487        /**
     1488         * Use this filter if you need to :
     1489         * - Load the avatar UI for a component that is !groups or !user (return true regarding your conditions)
     1490         * - Completely disable the avatar UI introduced in 2.3 (eg: __return_false())
     1491         *
     1492         * @since  BuddyPress (2.3.0)
     1493         *
     1494         * @var  bool whether to load the Avatar UI
     1495         */
     1496        return apply_filters( 'bp_avatar_is_front_edit', $retval );
     1497}
     1498
     1499/**
     1500 * Template function to load the Avatar UI javascript templates
     1501 *
     1502 * @since  BuddyPress (2.3.0)
     1503 */
     1504function bp_avatar_get_templates() {
     1505        if ( ! bp_avatar_is_front_edit() ) {
     1506                return;
     1507        }
     1508
     1509        bp_attachments_get_template_part( 'avatars/index' );
     1510}
     1511
     1512/**
     1513 * Trick to check if the theme's BuddyPress templates are up to date
     1514 *
     1515 * If the "avatar templates" are not including the new template tag, this will
     1516 * help users to get the avatar UI and inform the most curious that their
     1517 * templates are out of date.
     1518 *
     1519 * @since  BuddyPress (2.3.0)
     1520 */
     1521function bp_avatar_template_check() {
     1522        if ( ! bp_avatar_is_front_edit() ) {
     1523                return;
     1524        }
     1525
     1526        if ( ! did_action( 'bp_attachments_avatar_check_template' ) ) {
     1527                bp_attachments_get_template_part( 'avatars/index' );
     1528        }
     1529}
  • src/bp-core/bp-core-cssjs.php

    diff --git src/bp-core/bp-core-cssjs.php src/bp-core/bp-core-cssjs.php
    index 8694f8a..ea25396 100644
    function bp_core_register_common_scripts() { 
    2828        $scripts = apply_filters( 'bp_core_register_common_scripts', array(
    2929
    3030                // Legacy
    31                 'bp-confirm'        => array( 'file' => "{$url}confirm{$min}.js",        'dependencies' => array( 'jquery' ) ),
    32                 'bp-widget-members' => array( 'file' => "{$url}widget-members{$min}.js", 'dependencies' => array( 'jquery' ) ),
    33                 'bp-jquery-query'   => array( 'file' => "{$url}jquery-query{$min}.js",   'dependencies' => array( 'jquery' ) ),
    34                 'bp-jquery-cookie'  => array( 'file' => "{$url}jquery-cookie{$min}.js",  'dependencies' => array( 'jquery' ) ),
    35                 'bp-jquery-scroll-to' => array( 'file' => "{$url}jquery-scroll-to{$min}.js", 'dependencies' => array( 'jquery' ) ),
     31                'bp-confirm'        => array( 'file' => "{$url}confirm{$min}.js",        'dependencies' => array( 'jquery' ), 'footer' => false ),
     32                'bp-widget-members' => array( 'file' => "{$url}widget-members{$min}.js", 'dependencies' => array( 'jquery' ), 'footer' => false ),
     33                'bp-jquery-query'   => array( 'file' => "{$url}jquery-query{$min}.js",   'dependencies' => array( 'jquery' ), 'footer' => false ),
     34                'bp-jquery-cookie'  => array( 'file' => "{$url}jquery-cookie{$min}.js",  'dependencies' => array( 'jquery' ), 'footer' => false ),
     35                'bp-jquery-scroll-to' => array( 'file' => "{$url}jquery-scroll-to{$min}.js", 'dependencies' => array( 'jquery' ), 'footer' => false ),
    3636
    3737                // 2.1
    38                 'jquery-caret' => array( 'file' => "{$url}jquery.caret{$min}.js", 'dependencies' => array( 'jquery' ) ),
    39                 'jquery-atwho' => array( 'file' => "{$url}jquery.atwho{$min}.js", 'dependencies' => array( 'jquery', 'jquery-caret' ) ),
     38                'jquery-caret' => array( 'file' => "{$url}jquery.caret{$min}.js", 'dependencies' => array( 'jquery' ), 'footer' => false ),
     39                'jquery-atwho' => array( 'file' => "{$url}jquery.atwho{$min}.js", 'dependencies' => array( 'jquery', 'jquery-caret' ), 'footer' => false ),
     40
     41                // 2.3
     42                'bp-plupload' => array( 'file' => "{$url}bp-plupload{$min}.js", 'dependencies' => array( 'plupload', 'jquery', 'json2', 'wp-backbone' ), 'footer' => true ),
     43                'bp-avatar'   => array( 'file' => "{$url}avatar{$min}.js", 'dependencies' => array( 'jcrop' ), 'footer' => true ),
     44                'bp-webcam'   => array( 'file' => "{$url}webcam{$min}.js", 'dependencies' => array( 'bp-avatar' ), 'footer' => true ),
     45
    4046        ) );
    4147
    4248        $version = bp_get_version();
    4349        foreach ( $scripts as $id => $script ) {
    44                 wp_register_script( $id, $script['file'], $script['dependencies'], $version );
     50                wp_register_script( $id, $script['file'], $script['dependencies'], $version, $script['footer'] );
    4551        }
    4652}
    4753add_action( 'bp_enqueue_scripts',       'bp_core_register_common_scripts', 1 );
    function bp_core_register_common_styles() { 
    7682                'bp-admin-bar' => array(
    7783                        'file'         => $admin_bar_file,
    7884                        'dependencies' => array( 'admin-bar' )
    79                 )
     85                ),
     86                'bp-avatar' => array(
     87                        'file'         => "{$url}avatar{$min}.css",
     88                        'dependencies' => array( 'jcrop' )
     89                ),
    8090        ) );
    8191
    8292        foreach ( $styles as $id => $style ) {
    add_action( 'bp_enqueue_scripts', 'bp_core_confirmation_js' ); 
    110120add_action( 'bp_admin_enqueue_scripts', 'bp_core_confirmation_js' );
    111121
    112122/**
     123 * Enqueues the css and js required by the Avatar UI
     124 *
     125 * @since  BuddyPress (2.3.0)
     126 */
     127function bp_core_avatar_scripts() {
     128        if ( ! bp_avatar_is_front_edit() ) {
     129                return false;
     130        }
     131
     132        // Enqueue the Attachments scripts for the Avatar UI
     133        bp_attachments_enqueue_scripts( 'BP_Attachment_Avatar' );
     134
     135        // Add Some actions for Theme backcompat
     136        add_action( 'bp_after_profile_avatar_upload_content', 'bp_avatar_template_check' );
     137        add_action( 'bp_after_group_admin_content',           'bp_avatar_template_check' );
     138        add_action( 'bp_after_group_avatar_creation_step',    'bp_avatar_template_check' );
     139}
     140add_action( 'bp_enqueue_scripts', 'bp_core_avatar_scripts' );
     141
     142/**
    113143 * Enqueues jCrop library and hooks BP's custom cropper JS.
    114144 */
    115145function bp_core_add_jquery_cropper() {
  • 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 14e1f6c..8dd8edc 100644
    class BP_Attachment_Avatar extends BP_Attachment { 
    256256                // Return the full and thumb cropped avatars
    257257                return $avatar_types;
    258258        }
     259
     260        /**
     261         * Get the user id to set its avatar
     262         *
     263         * @since BuddyPress (2.3.0)
     264         *
     265         * @return integer the user ID
     266         */
     267        private function get_user_id() {
     268                $bp = buddypress();
     269                $user_id = 0;
     270
     271                if ( bp_is_user() ) {
     272                        $user_id = bp_displayed_user_id();
     273                }
     274
     275                if ( ! empty( $bp->members->admin->user_id ) ) {
     276                        $user_id = $bp->members->admin->user_id;
     277                }
     278
     279                return $user_id;
     280        }
     281
     282        /**
     283         * Get the group id to set its avatar
     284         *
     285         * @since BuddyPress (2.3.0)
     286         *
     287         * @return integer the group id
     288         */
     289        private function get_group_id() {
     290                $group_id = 0;
     291
     292                if ( bp_is_group() ) {
     293                        $group_id = bp_get_current_group_id();
     294                }
     295
     296                return $group_id;
     297        }
     298
     299        /**
     300         * Build script datas for the Uploader UI
     301         *
     302         * @since BuddyPress (2.3.0)
     303         *
     304         * @return array the javascript localization data
     305         */
     306        public function script_data() {
     307                // Get default script data
     308                $script_data = parent::script_data();
     309
     310                // Defaults to Avatar Backbone script
     311                $js_scripts = array( 'bp-avatar' );
     312
     313                // Default object
     314                $object = '';
     315
     316                // Get the possible item ids
     317                $user_id  = $this->get_user_id();
     318                $group_id = $this->get_group_id();
     319
     320                if ( ! empty( $user_id ) ) {
     321                        // Use this filter to disable the Webcam Avatar feature
     322                        if ( false !== apply_filters( 'bp_attachment_avatar_use_webcam', true ) ) {
     323                                $js_scripts = array( 'bp-webcam' );
     324                        }
     325
     326                        $script_data['bp_params'] = array(
     327                                'object'     => 'user',
     328                                'item_id'    => $user_id,
     329                                'has_avatar' => bp_get_user_has_avatar( $user_id ),
     330                                'nonces'  => array(
     331                                        'set'    => wp_create_nonce( 'bp_avatar_cropstore' ),
     332                                        'remove' => wp_create_nonce( 'bp_delete_avatar_link' ),
     333                                ),
     334                        );
     335
     336                        // Set feedback messages
     337                        $script_data['feedback_messages'] = array(
     338                                1 => __( 'There was a problem cropping your profile photo.', 'buddypress' ),
     339                                2 => __( 'Your new profile photo was uploaded successfully.', 'buddypress' ),
     340                                3 => __( 'There was a problem deleting your profile photo. Please try again.', 'buddypress' ),
     341                                4 => __( 'Your profile photo was deleted successfully!', 'buddypress' ),
     342                        );
     343                } elseif ( ! empty( $group_id ) ) {
     344                        $script_data['bp_params'] = array(
     345                                'object'     => 'group',
     346                                'item_id'    => $group_id,
     347                                'has_avatar' => bp_get_group_has_avatar( $group_id ),
     348                                'nonces'     => array(
     349                                        'set'    => wp_create_nonce( 'bp_avatar_cropstore' ),
     350                                        'remove' => wp_create_nonce( 'bp_group_avatar_delete' ),
     351                                ),
     352                        );
     353
     354                        // Set feedback messages
     355                        $script_data['feedback_messages'] = array(
     356                                1 => __( 'There was a problem cropping the group profile photo.', 'buddypress' ),
     357                                2 => __( 'The group profile photo was uploaded successfully.', 'buddypress' ),
     358                                3 => __( 'There was a problem deleting the group profile photo. Please try again.', 'buddypress' ),
     359                                4 => __( 'The group profile photo was deleted successfully!', 'buddypress' ),
     360                        );
     361                } else {
     362                        /**
     363                         * Use this filter to include specific BuddyPress params for your object
     364                         * e.g. Blavatar
     365                         *
     366                         * @since BuddyPress (2.3.0)
     367                         *
     368                         * @param array the avatar specific BuddyPress parameters
     369                         */
     370                        $script_data['bp_params'] = apply_filters( 'bp_attachment_avatar_params', array() );
     371                }
     372
     373                // Include the specific css
     374                $script_data['extra_css'] = array( 'bp-avatar' );
     375
     376                // Include the specific css
     377                $script_data['extra_js']  = $js_scripts;
     378
     379                // Set the object to contextualize the filter
     380                if ( isset( $script_data['bp_params']['object'] ) ) {
     381                        $object = $script_data['bp_params']['object'];
     382                }
     383
     384                /**
     385                 * Use this filter to override/extend the avatar script data
     386                 *
     387                 * @since BuddyPress (2.3.0)
     388                 *
     389                 * @param array  $script_data the avatar script data
     390                 * @param string $object      the object the avatar belongs to (eg: user or group)
     391                 */
     392                return apply_filters( 'bp_attachment_avatar_script_data', $script_data, $object );
     393        }
    259394}
  • 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 6a7d115..4d6365d 100644
    abstract class BP_Attachment { 
    477477                // Finally crop the image
    478478                return wp_crop_image( $r['original_file'], (int) $r['crop_x'], (int) $r['crop_y'], (int) $r['crop_w'], (int) $r['crop_h'], (int) $r['dst_w'], (int) $r['dst_h'], $r['src_abs'], $r['dst_file'] );
    479479        }
     480
     481        /**
     482         * Build script datas for the Uploader UI
     483         *
     484         * Override this method from your child class to build the script datas
     485         *
     486         * @since BuddyPress (2.3.0)
     487         *
     488         * @return array the javascript localization data
     489         */
     490        public function script_data() {
     491                $script_data = array(
     492                        'action'            => $this->action,
     493                        'file_data_name'    => $this->file_input,
     494                        'max_file_size'     => $this->original_max_filesize,
     495                        'feedback_messages' => array(
     496                                1 => __( 'Sorry, uploading the file failed.', 'buddypress' ),
     497                                2 => __( 'File successfully uploaded.', 'buddypress' ),
     498                        ),
     499                );
     500
     501                return $script_data;
     502        }
    480503}
  • src/bp-core/css/avatar.css

    diff --git src/bp-core/css/avatar.css src/bp-core/css/avatar.css
    index e69de29..32dceaa 100644
     
     1div.bp-avatar-status {
     2        clear:both;
     3        margin:1em 0;
     4}
     5
     6div.bp-avatar-status p.updated {
     7        display: block;
     8        padding: 10px 15px;
     9}
     10
     11div.bp-avatar-status p.success {
     12        background-color: #efc;
     13        border: 1px solid #591;
     14        color: #250;
     15}
     16
     17div.bp-avatar-status p.error {
     18        background-color: #fdc;
     19        border: 1px solid #a00;
     20        color: #800;
     21}
     22
     23.progress {
     24        float: right;
     25        height: 22px;
     26        margin: 6px 10px 0 0;
     27        width: 200px;
     28        line-height: 2em;
     29        padding: 0;
     30        overflow: hidden;
     31        margin-bottom: 2px;
     32        border: 1px solid #d1d1d1;
     33        background: none;
     34}
     35
     36.bar {
     37        z-index: 9;
     38        width: 0;
     39        height: 100%;
     40        background-color: #c3ff88;
     41}
     42
     43.bp-uploader-progress div.error {
     44        font-size: 90%;
     45        display: block;
     46        padding: 10px 15px;
     47        background-color: #fdc;
     48        border: 1px solid #a00;
     49        color: #800;
     50}
     51
     52#bp-uploader-warning, #bp-webcam-message p.warning {
     53        margin:1em 0;
     54        font-size: 90%;
     55        display: block;
     56        padding: 10px 15px;
     57        background-color: #ffec8b;
     58        border: 1px solid #fc0;
     59        color: #440;
     60}
     61
     62div.bp-avatar-nav {
     63        clear:both;
     64        background: transparent;
     65        margin: 10px 0 10px;
     66        overflow: hidden;
     67}
     68
     69.avatar-nav-items {
     70        margin: 0;
     71        padding: 0;
     72}
     73
     74.bp-avatar-nav .avatar-nav-items li.avatar-nav-item {
     75        float: left;
     76        margin: 0;
     77        list-style: none;
     78 }
     79
     80.avatar-nav-items li a {
     81        display: block;
     82        padding: 5px 10px;
     83        text-decoration: none;
     84}
     85
     86.avatar-nav-items li.current a {
     87        background-color: #eee;
     88        color: #555;
     89        opacity: .8;
     90        font-weight: bold;
     91}
     92
     93#drag-drop-area {
     94        border: 4px dashed #bbb;
     95        height: 200px;
     96}
     97
     98.drag-drop.drag-over #drag-drop-area {
     99        border-color: #83b4d8;
     100}
     101
     102.drag-drop-inside p {
     103        display:none;
     104}
     105
     106.drag-drop-inside p.drag-drop-buttons {
     107        margin-top:80px;
     108        text-align: center;
     109}
     110.drag-drop .drag-drop-inside p.drag-drop-buttons {
     111        margin:auto;
     112        text-align: auto;
     113}
     114
     115.drag-drop .drag-drop-inside {
     116        margin: 70px auto 0;
     117        width: 250px;
     118}
     119
     120.drag-drop .drag-drop-inside p, .drag-drop-inside p.drag-drop-buttons {
     121        display: block;
     122}
     123
     124.drag-drop .drag-drop-inside p {
     125        color: #aaa;
     126        font-size: 110%;
     127        margin: 5px 0;
     128        text-align: center;
     129}
     130
     131#avatar-to-crop {
     132        float: left;
     133        margin: 0 20px 20px 0;
     134        text-align: left;
     135}
     136#avatar-crop-pane {
     137        overflow: hidden;
     138}
     139
     140#avatar-crop-actions {
     141        margin: 20px 0;
     142}
     143
     144#avatar-to-crop img,
     145#avatar-crop-pane img,
     146#avatar-crop-pane canvas,
     147#avatar-upload-form img,
     148#create-group-form img,
     149#group-settings-form img {
     150        border: none !important;
     151        max-width: none !important;
     152}
     153
     154#bp-webcam-avatar video {
     155        float:left;
     156        width:450px;
     157}
     158
     159#bp-webcam-avatar #avatar-crop-pane {
     160        border: 2px dashed #bbb;
     161}
     162
     163/** Admin Profile **/
     164body.users_page_bp-profile-edit.modal-open #TB_ajaxContent,
     165body.profile_page_bp-profile-edit.modal-open #TB_ajaxContent {
     166        width:95%!important;
     167        height:95%!important;
     168}
     169
     170body.users_page_bp-profile-edit.modal-open #TB_ajaxContent p.updated,
     171body.users_page_bp-profile-edit.modal-open #TB_ajaxContent p.warning,
     172body.profile_page_bp-profile-edit.modal-open #TB_ajaxContent p.updated,
     173body.profile_page_bp-profile-edit.modal-open #TB_ajaxContent p.warning {
     174        display: block;
     175        padding: 10px 15px;
     176}
  • src/bp-core/js/avatar.js

    diff --git src/bp-core/js/avatar.js src/bp-core/js/avatar.js
    index e69de29..7d592f7 100644
     
     1/* globals bp, BP_Uploader, _, Backbone */
     2
     3window.bp = window.bp || {};
     4
     5( function( exports, $ ) {
     6
     7        // Bail if not set
     8        if ( typeof BP_Uploader === 'undefined' ) {
     9                return;
     10        }
     11
     12        bp.Models      = bp.Models || {};
     13        bp.Collections = bp.Collections || {};
     14        bp.Views       = bp.Views || {};
     15
     16        bp.Avatar = {
     17                start: function() {
     18                        /**
     19                         * Remove the bp-legacy UI
     20                         *
     21                         * bp.Avatar successfully loaded, we can now
     22                         * safely remove the Legacy UI.
     23                         */
     24                        this.removeLegacyUI();
     25
     26                        // Init some vars
     27                        this.views    = new Backbone.Collection();
     28                        this.jcropapi = {};
     29
     30                        // Set up nav
     31                        this.setupNav();
     32
     33                        // Avatars are uploaded files
     34                        this.avatars = bp.Uploader.filesUploaded;
     35
     36                        // Wait till the queue is reset
     37                        bp.Uploader.filesQueue.on( 'reset', this.cropView, this );
     38
     39                        /**
     40                         * In Administration screens we're using Thickbox
     41                         * We need to make sure to reset the views if it's closed
     42                         */
     43                        $( 'body.wp-admin' ).on( 'tb_unload', '#TB_window', function() {
     44                                // Reset to the uploader view
     45                                bp.Avatar.nav.trigger( 'bp-avatar-view:changed', 'upload' );
     46
     47                                // Reset to the uploader nav
     48                                _.each( bp.Avatar.navItems.models, function( model ) {
     49                                        if ( model.id === 'upload' ) {
     50                                                model.set( { active: 1 } );
     51                                        } else {
     52                                                model.set( { active: 0 } );
     53                                        }
     54                                } );
     55                        } );
     56                },
     57
     58                removeLegacyUI: function() {
     59                        // User
     60                        if ( $( '#avatar-upload-form' ).length ) {
     61                                $( '#avatar-upload' ).remove();
     62                                $( '#avatar-upload-form p' ).remove();
     63
     64                        // Group Manage
     65                        } else if ( $( '#group-settings-form' ).length ) {
     66                                $( '#group-settings-form p' ).each( function( i ) {
     67                                        if ( 0 !== i ) {
     68                                                $( this ).remove();
     69                                        }
     70                                } );
     71
     72                                if ( $( '#delete-group-avatar-button' ).length ) {
     73                                        $( '#delete-group-avatar-button' ).remove();
     74                                }
     75
     76                        // Group Create
     77                        } else if ( $( '#group-create-body' ).length ) {
     78                                $( '.main-column p #file' ).remove();
     79                                $( '.main-column p #upload' ).remove();
     80
     81                        // Admin Extended Profile
     82                        } else if ( $( '#bp_xprofile_user_admin_avatar a.bp-xprofile-avatar-user-admin' ).length ) {
     83                                $( '#bp_xprofile_user_admin_avatar a.bp-xprofile-avatar-user-admin' ).remove();
     84                        }
     85                },
     86
     87                setView: function( view ) {
     88                        // Clear views
     89                        if ( ! _.isUndefined( this.views.models ) ) {
     90                                _.each( this.views.models, function( model ) {
     91                                        model.get( 'view' ).remove();
     92                                }, this );
     93                        }
     94
     95                        // Reset Views
     96                        this.views.reset();
     97
     98                        // Reset Avatars (file uploaded)
     99                        if ( ! _.isUndefined( this.avatars ) ) {
     100                                this.avatars.reset();
     101                        }
     102
     103                        // Reset the Jcrop API
     104                        if ( ! _.isEmpty( this.jcropapi ) ) {
     105                                this.jcropapi.destroy();
     106                                this.jcropapi = {};
     107                        }
     108
     109                        // Load the required view
     110                        switch ( view ) {
     111                                case 'upload':
     112                                        this.uploaderView();
     113                                        break;
     114
     115                                case 'delete':
     116                                        this.deleteView();
     117                                        break;
     118                        }
     119                },
     120
     121                setupNav: function() {
     122                        var self = this,
     123                            initView, activeView;
     124
     125                        this.navItems = new Backbone.Collection();
     126
     127                        _.each( BP_Uploader.settings.nav, function( item, index ) {
     128                                if ( ! _.isObject( item ) ) {
     129                                        return;
     130                                }
     131
     132                                // Reset active View
     133                                activeView = 0;
     134
     135                                if ( 0 === index ) {
     136                                        initView = item.id;
     137                                        activeView = 1;
     138                                }
     139
     140                                self.navItems.add( {
     141                                        id     : item.id,
     142                                        name   : item.caption,
     143                                        href   : '#',
     144                                        active : activeView,
     145                                        hide   : _.isUndefined( item.hide ) ? 0 : item.hide
     146                                } );
     147                        } );
     148
     149                        this.nav = new bp.Views.Nav( { collection: this.navItems } );
     150                        this.nav.inject( '.bp-avatar-nav' );
     151
     152                        // Activate the initial view (uploader)
     153                        this.setView( initView );
     154
     155                        // Listen to nav changes (it's like a do_action!)
     156                        this.nav.on( 'bp-avatar-view:changed', _.bind( this.setView, this ) );
     157                },
     158
     159                uploaderView: function() {
     160                        // Listen to the Queued uploads
     161                        bp.Uploader.filesQueue.on( 'add', this.uploadProgress, this );
     162
     163                        // Create the BuddyPress Uploader
     164                        var uploader = new bp.Views.Uploader();
     165
     166                        // Add it to views
     167                        this.views.add( { id: 'upload', view: uploader } );
     168
     169                        // Display it
     170                        uploader.inject( '.bp-avatar' );
     171                },
     172
     173                uploadProgress: function() {
     174                        // Create the Uploader status view
     175                        var avatarStatus = new bp.Views.uploaderStatus( { collection: bp.Uploader.filesQueue } );
     176
     177                        if ( ! _.isUndefined( this.views.get( 'status' ) ) ) {
     178                                this.views.set( { id: 'status', view: avatarStatus } );
     179                        } else {
     180                                this.views.add( { id: 'status', view: avatarStatus } );
     181                        }
     182
     183                        // Display it
     184                        avatarStatus.inject( '.bp-avatar-status' );
     185                },
     186
     187                cropView: function() {
     188                        var status;
     189
     190                        // Bail there was an error during the Upload
     191                        if ( _.isEmpty( this.avatars.models ) ) {
     192                                return;
     193                        }
     194
     195                        // Make sure to remove the uploads status
     196                        if ( ! _.isUndefined( this.views.get( 'status' ) ) ) {
     197                                status = this.views.get( 'status' );
     198                                status.get( 'view' ).remove();
     199                                this.views.remove( { id: 'status', view: status } );
     200                        }
     201
     202                        // Create the Avatars view
     203                        var avatar = new bp.Views.Avatars( { collection: this.avatars } );
     204                        this.views.add( { id: 'crop', view: avatar } );
     205
     206                        avatar.inject( '.bp-avatar' );
     207                },
     208
     209                setAvatar: function( avatar ) {
     210                        var self = this,
     211                                crop;
     212
     213                        // Remove the crop view
     214                        if ( ! _.isUndefined( this.views.get( 'crop' ) ) ) {
     215                                // Remove the JCrop API
     216                                if ( ! _.isEmpty( this.jcropapi ) ) {
     217                                        this.jcropapi.destroy();
     218                                        this.jcropapi = {};
     219                                }
     220                                crop = this.views.get( 'crop' );
     221                                crop.get( 'view' ).remove();
     222                                this.views.remove( { id: 'crop', view: crop } );
     223                        }
     224
     225                        // Set the avatar !
     226                        bp.ajax.post( 'bp_avatar_set', {
     227                                json:          true,
     228                                original_file: avatar.get( 'url' ),
     229                                crop_w:        avatar.get( 'w' ),
     230                                crop_h:        avatar.get( 'h' ),
     231                                crop_x:        avatar.get( 'x' ),
     232                                crop_y:        avatar.get( 'y' ),
     233                                item_id:       avatar.get( 'item_id' ),
     234                                object:        avatar.get( 'object' ),
     235                                type:          _.isUndefined( avatar.get( 'type' ) ) ? 'crop' : avatar.get( 'type' ),
     236                                nonce:         avatar.get( 'nonces' ).set
     237                        } ).done( function( response ) {
     238                                var avatarStatus = new bp.Views.AvatarStatus( {
     239                                        value : BP_Uploader.strings.feedback_messages[ response.feedback_code ],
     240                                        type : 'success'
     241                                } );
     242
     243                                self.views.add( {
     244                                        id   : 'status',
     245                                        view : avatarStatus
     246                                } );
     247
     248                                avatarStatus.inject( '.bp-avatar-status' );
     249
     250                                // Update each avatars of the page
     251                                $( '.' + avatar.get( 'object' ) + '-' + response.item_id + '-avatar' ).each( function() {
     252                                        $(this).prop( 'src', response.avatar );
     253                                } );
     254
     255                                // Inject the Delete nav
     256                                bp.Avatar.navItems.get( 'delete' ).set( { hide: 0 } );
     257
     258                        } ).fail( function( response ) {
     259                                var feedback = BP_Uploader.strings.default_error;
     260                                if ( ! _.isUndefined( response ) ) {
     261                                        feedback = BP_Uploader.strings.feedback_messages[ response.feedback_code ];
     262                                }
     263
     264                                var avatarStatus = new bp.Views.AvatarStatus( {
     265                                        value : feedback,
     266                                        type : 'error'
     267                                } );
     268
     269                                self.views.add( {
     270                                        id   : 'status',
     271                                        view : avatarStatus
     272                                } );
     273
     274                                avatarStatus.inject( '.bp-avatar-status' );
     275                        } );
     276                },
     277
     278                deleteView:function() {
     279                        // Create the delete model
     280                        var delete_model = new Backbone.Model( _.pick( BP_Uploader.settings.defaults.multipart_params.bp_params,
     281                                'object',
     282                                'item_id',
     283                                'nonces'
     284                        ) );
     285
     286                        // Create the delete view
     287                        var deleteView = new bp.Views.DeleteAvatar( { model: delete_model } );
     288
     289                        // Add it to views
     290                        this.views.add( { id: 'delete', view: deleteView } );
     291
     292                        // Display it
     293                        deleteView.inject( '.bp-avatar' );
     294                },
     295
     296                deleteAvatar: function( model ) {
     297                        var self = this,
     298                                deleteView;
     299
     300                        // Remove the delete view
     301                        if ( ! _.isUndefined( this.views.get( 'delete' ) ) ) {
     302                                deleteView = this.views.get( 'delete' );
     303                                deleteView.get( 'view' ).remove();
     304                                this.views.remove( { id: 'delete', view: deleteView } );
     305                        }
     306
     307                        // Remove the avatar !
     308                        bp.ajax.post( 'bp_avatar_delete', {
     309                                json:          true,
     310                                item_id:       model.get( 'item_id' ),
     311                                object:        model.get( 'object' ),
     312                                nonce:         model.get( 'nonces' ).remove
     313                        } ).done( function( response ) {
     314                                var avatarStatus = new bp.Views.AvatarStatus( {
     315                                        value : BP_Uploader.strings.feedback_messages[ response.feedback_code ],
     316                                        type : 'success'
     317                                } );
     318
     319                                self.views.add( {
     320                                        id   : 'status',
     321                                        view : avatarStatus
     322                                } );
     323
     324                                avatarStatus.inject( '.bp-avatar-status' );
     325
     326                                // Update each avatars of the page
     327                                $( '.' + model.get( 'object' ) + '-' + response.item_id + '-avatar').each( function() {
     328                                        $( this ).prop( 'src', response.avatar );
     329                                } );
     330
     331                                 // Remove the Delete nav
     332                                 bp.Avatar.navItems.get( 'delete' ).set( { active: 0, hide: 1 } );
     333
     334                        } ).fail( function( response ) {
     335                                var feedback = BP_Uploader.strings.default_error;
     336                                if ( ! _.isUndefined( response ) ) {
     337                                        feedback = BP_Uploader.strings.feedback_messages[ response.feedback_code ];
     338                                }
     339
     340                                var avatarStatus = new bp.Views.AvatarStatus( {
     341                                        value : feedback,
     342                                        type : 'error'
     343                                } );
     344
     345                                self.views.add( {
     346                                        id   : 'status',
     347                                        view : avatarStatus
     348                                } );
     349
     350                                avatarStatus.inject( '.bp-avatar-status' );
     351                        } );
     352                }
     353        };
     354
     355        // Main Nav view
     356        bp.Views.Nav = bp.View.extend( {
     357                tagName:    'ul',
     358                className:  'avatar-nav-items',
     359
     360                events: {
     361                        'click .bp-avatar-nav-item' : 'toggleView'
     362                },
     363
     364                initialize: function() {
     365                        _.each( this.collection.models, this.addNavItem, this );
     366                        this.collection.on( 'change:hide', this.showHideNavItem, this );
     367                },
     368
     369                addNavItem: function( item ) {
     370                        /**
     371                         * The delete nav is not added if no avatar
     372                         * is set for the object
     373                         */
     374                        if ( 1 === item.get( 'hide' ) ) {
     375                                return;
     376                        }
     377
     378                        this.views.add( new bp.Views.NavItem( { model: item } ) );
     379                },
     380
     381                showHideNavItem: function( item ) {
     382                        var isRendered = null;
     383
     384                        /**
     385                         * Loop in views to show/hide the nav item
     386                         * BuddyPress is only using this for the delete nav
     387                         */
     388                        _.each( this.views._views[''], function( view ) {
     389                                if ( 1 === view.model.get( 'hide' ) ) {
     390                                        view.remove();
     391                                }
     392
     393                                // Check to see if the nav is not already rendered
     394                                if ( item.get( 'id' ) === view.model.get( 'id' ) ) {
     395                                        isRendered = true;
     396                                }
     397                        } );
     398
     399                        // Add the Delete nav if not rendered
     400                        if ( ! _.isBoolean( isRendered ) ) {
     401                                this.addNavItem( item );
     402                        }
     403                },
     404
     405                toggleView: function( event ) {
     406                        event.preventDefault();
     407
     408                        var active = $( event.target ).data( 'nav' );
     409
     410                        _.each( this.collection.models, function( model ) {
     411                                if ( model.id === active ) {
     412                                        model.set( { active: 1 } );
     413                                        this.trigger( 'bp-avatar-view:changed', model.id );
     414                                } else {
     415                                        model.set( { active: 0 } );
     416                                }
     417                        }, this );
     418                }
     419        } );
     420
     421        // Nav item view
     422        bp.Views.NavItem = bp.View.extend( {
     423                tagName:    'li',
     424                className:  'avatar-nav-item',
     425                template: bp.template( 'bp-avatar-nav' ),
     426
     427                initialize: function() {
     428                        if ( 1 === this.model.get( 'active' ) ) {
     429                                this.el.className += ' current';
     430                        }
     431                        this.el.id += 'bp-avatar-' + this.model.get( 'id' );
     432
     433                        this.model.on( 'change:active', this.setCurrentNav, this );
     434                },
     435
     436                setCurrentNav: function( model ) {
     437                        if ( 1 === model.get( 'active' ) ) {
     438                                this.$el.addClass( 'current' );
     439                        } else {
     440                                this.$el.removeClass( 'current' );
     441                        }
     442                }
     443        } );
     444
     445        // Avatars view
     446        bp.Views.Avatars = bp.View.extend( {
     447                className: 'items',
     448
     449                initialize: function() {
     450                        _.each( this.collection.models, this.addItemView, this );
     451                },
     452
     453                addItemView: function( item ) {
     454                        // Defaults to 150
     455                        var full_d = { full_h: 150, full_w: 150 };
     456
     457                        // Make sure to take in account bp_core_avatar_full_height or bp_core_avatar_full_width php filters
     458                        if ( ! _.isUndefined( BP_Uploader.settings.crop.full_h ) && ! _.isUndefined( BP_Uploader.settings.crop.full_w ) ) {
     459                                full_d.full_h = BP_Uploader.settings.crop.full_h;
     460                                full_d.full_w = BP_Uploader.settings.crop.full_w;
     461                        }
     462
     463                        // Set the avatar model
     464                        item.set( _.extend( _.pick( BP_Uploader.settings.defaults.multipart_params.bp_params,
     465                                'object',
     466                                'item_id',
     467                                'nonces'
     468                        ), full_d ) );
     469
     470                        // Add the view
     471                        this.views.add( new bp.Views.Avatar( { model: item } ) );
     472                }
     473        } );
     474
     475        // Avatar view
     476        bp.Views.Avatar = bp.View.extend( {
     477                className: 'item',
     478                template: bp.template( 'bp-avatar-item' ),
     479
     480                events: {
     481                        'click .avatar-crop-submit': 'cropAvatar'
     482                },
     483
     484                initialize: function() {
     485                        _.defaults( this.options, {
     486                                full_h:  BP_Uploader.settings.crop.full_h,
     487                                full_w:  BP_Uploader.settings.crop.full_w,
     488                                aspectRatio : 1
     489                        } );
     490
     491                        this.on( 'ready', this.initCropper );
     492                },
     493
     494                initCropper: function() {
     495                        var self = this,
     496                                tocrop = this.$el.find( '#avatar-to-crop img' ),
     497                                selection = {}, crop_top, crop_bottom, crop_left, crop_right, nh, nw;
     498
     499                        if ( ! _.isUndefined( this.options.full_h ) && ! _.isUndefined( this.options.full_w ) ) {
     500                                this.options.aspectRatio = this.options.full_h / this.options.full_w;
     501                        }
     502
     503                        selection.w = this.model.get( 'width' );
     504                        selection.h = this.model.get( 'height' );
     505
     506                        if ( selection.h <= selection.w ) {
     507                                crop_top    = Math.round( selection.h / 4 );
     508                                nh = nw     = Math.round( selection.h / 2 );
     509                                crop_bottom = nh + crop_top;
     510                                crop_left   = ( selection.w - nw ) / 2;
     511                                crop_right  = nw + crop_left;
     512                        } else {
     513                                crop_left   = Math.round( selection.w / 4 );
     514                                nh = nw     = Math.round( selection.w / 2 );
     515                                crop_right  = nw + crop_left;
     516                                crop_top    = ( selection.h - nh ) / 2;
     517                                crop_bottom = nh + crop_top;
     518                        }
     519
     520                        // Add the cropping interface
     521                        tocrop.Jcrop( {
     522                                onChange: _.bind( self.showPreview, self ),
     523                                onSelect: _.bind( self.showPreview, self ),
     524                                aspectRatio: self.options.aspectRatio,
     525                                setSelect: [ crop_left, crop_top, crop_right, crop_bottom ]
     526                        }, function() {
     527                                // Get the Jcrop API
     528                                bp.Avatar.jcropapi = this;
     529                        } );
     530                },
     531
     532                cropAvatar: function( event ) {
     533                        event.preventDefault();
     534
     535                        bp.Avatar.setAvatar( this.model );
     536                },
     537
     538                showPreview: function( coords ) {
     539                        if ( ! coords.w || ! coords.h ) {
     540                                return;
     541                        }
     542
     543                        if ( parseInt( coords.w, 10 ) > 0 ) {
     544                                var fw = this.options.full_w;
     545                                var fh = this.options.full_h;
     546                                var rx = fw / coords.w;
     547                                var ry = fh / coords.h;
     548
     549                                // Update the model
     550                                this.model.set( { x: coords.x, y: coords.y, w: coords.w, h: coords.h } );
     551
     552                                $( '#avatar-crop-preview' ).css( {
     553                                        maxWidth:'none',
     554                                        width: Math.round( rx *  this.model.get( 'width' ) )+ 'px',
     555                                        height: Math.round( ry * this.model.get( 'height' ) )+ 'px',
     556                                        marginLeft: '-' + Math.round( rx * this.model.get( 'x' ) ) + 'px',
     557                                        marginTop: '-' + Math.round( ry * this.model.get( 'y' ) ) + 'px'
     558                                } );
     559                        }
     560                }
     561        } );
     562
     563        // BuddyPress Avatar Feedback view
     564        bp.Views.AvatarStatus = bp.View.extend( {
     565                tagName: 'p',
     566                className: 'updated',
     567                id: 'bp-avatar-feedback',
     568
     569                initialize: function() {
     570                        this.el.className += ' ' + this.options.type;
     571                        this.value = this.options.value;
     572                },
     573
     574                render: function() {
     575                        this.$el.html( this.value );
     576                        return this;
     577                }
     578        } );
     579
     580        // BuddyPress Avatar Delete view
     581        bp.Views.DeleteAvatar = bp.View.extend( {
     582                tagName: 'div',
     583                id: 'bp-delete-avatar',
     584                template: bp.template( 'bp-avatar-delete' ),
     585
     586                events: {
     587                        'click #bp-delete-avatar': 'deleteAvatar'
     588                },
     589
     590                deleteAvatar: function( event ) {
     591                        event.preventDefault();
     592
     593                        bp.Avatar.deleteAvatar( this.model );
     594                }
     595        } );
     596
     597        bp.Avatar.start();
     598
     599})( bp, jQuery );
  • src/bp-core/js/bp-plupload.js

    diff --git src/bp-core/js/bp-plupload.js src/bp-core/js/bp-plupload.js
    index e69de29..ce22622 100644
     
     1/* globals bp, plupload, BP_Uploader, _, JSON, Backbone */
     2
     3window.wp = window.wp || {};
     4window.bp = window.bp || window.wp;
     5
     6( function( exports, $ ) {
     7
     8        // Bail if not set
     9        if ( typeof BP_Uploader === 'undefined' ) {
     10                return;
     11        }
     12
     13        bp.Models      = bp.Models || {};
     14        bp.Collections = bp.Collections || {};
     15        bp.Views       = bp.Views || {};
     16        bp.Uploader    = {};
     17
     18        /**
     19         * BuddyPress Uploader.
     20         *
     21         * This is an adapted version of wp.Uploader
     22         */
     23        bp.Uploader.uploader = function() {
     24                var self = this,
     25                        isIE = navigator.userAgent.indexOf('Trident/') !== -1 || navigator.userAgent.indexOf('MSIE ') !== -1;
     26
     27                this.params  = BP_Uploader.settings;
     28                this.strings = BP_Uploader.strings;
     29
     30                this.supports = {
     31                        upload: this.params.browser.supported
     32                };
     33
     34                this.supported = this.supports.upload;
     35
     36                if ( ! this.supported ) {
     37                        /*jshint -W020 */
     38                        BP_Uploader = undefined;
     39                        return;
     40                }
     41
     42                // Make sure flash sends cookies (seems in IE it does without switching to urlstream mode)
     43                if ( ! isIE && 'flash' === plupload.predictRuntime( this.params.defaults ) &&
     44                        ( ! this.params.defaults.required_features || ! this.params.defaults.required_features.hasOwnProperty( 'send_binary_string' ) ) ) {
     45
     46                        this.params.defaults.required_features = this.params.defaults.required_features || {};
     47                        this.params.defaults.required_features.send_binary_string = true;
     48                }
     49
     50                this.uploader = new plupload.Uploader( this.params.defaults );
     51
     52                /**
     53                 * After the Uploader has been initialized, initialize some behaviors for the dropzone.
     54                 *
     55                 * @event Init
     56                 * @param {plupload.Uploader} uploader Uploader instance.
     57                 */
     58                this.uploader.bind( 'Init', function( uploader ) {
     59                        var container = $( '#' + self.params.defaults.container ),
     60                            drop_element = $( '#' + self.params.defaults.drop_element );
     61
     62                        if ( 'html4' === uploader.runtime ) {
     63                                uploader.settings.multipart_params.html4 = true;
     64                        }
     65
     66                        if ( uploader.features.dragdrop && ! self.params.browser.mobile ) {
     67                                container.addClass( 'drag-drop' );
     68                                drop_element.bind( 'dragover.wp-uploader', function() {
     69                                        container.addClass( 'drag-over' );
     70                                } ).bind( 'dragleave.wp-uploader, drop.wp-uploader', function() {
     71                                        container.removeClass( 'drag-over' );
     72                                } );
     73                        } else {
     74                                container.removeClass( 'drag-drop' );
     75                                drop_element.unbind( '.wp-uploader' );
     76                        }
     77
     78                } );
     79
     80                // Init BuddyPress Uploader
     81                this.uploader.init();
     82
     83                /**
     84                 * Feedback callback.
     85                 *
     86                 * Add a new message to the errors collection, so it's possible
     87                 * to give some feedback to the user
     88                 *
     89                 * @param  {string}        message
     90                 * @param  {object}        data
     91                 * @param  {plupload.File} file     File that was uploaded.
     92                 */
     93                this.feedback = function( message, data, file ) {
     94                        if ( ! _.isNull( file ) && file.item ) {
     95                                file.item.clear();
     96                        }
     97
     98                        bp.Uploader.filesError.unshift( {
     99                                message: message,
     100                                data:    data,
     101                                file:    file
     102                        } );
     103                };
     104
     105                /**
     106                 * After files were filtered and added to the queue, create a model for each.
     107                 *
     108                 * @event FilesAdded
     109                 * @param {plupload.Uploader} uploader Uploader instance.
     110                 * @param {Array}             files    Array of file objects that were added to queue by the user.
     111                 */
     112                this.uploader.bind( 'FilesAdded', function( uploader, files ) {
     113                        var hundredmb = 100 * 1024 * 1024, max = parseInt( uploader.settings.max_file_size, 10 ),
     114                            _this = this;
     115
     116                        /**
     117                         * In case the multiple selection is false (eg: avatar) stop the process and send
     118                         * and event containing a warning
     119                         */
     120                        if ( ! uploader.settings.multi_selection && files.length > 1 ) {
     121                                for ( var i in files ) {
     122                                        uploader.removeFile( files[i] );
     123                                }
     124
     125                                $( self ).trigger( 'bp-uploader-warning', self.strings.unique_file_warning );
     126                                return;
     127                        }
     128
     129                        _.each( files, function( file ) {
     130                                var attributes;
     131
     132                                // Ignore failed uploads.
     133                                if ( plupload.FAILED === file.status ) {
     134                                        return;
     135                                }
     136
     137                                if ( max > hundredmb && file.size > hundredmb && uploader.runtime !== 'html5' ) {
     138                                        _this.uploadSizeError( uploader, file, true );
     139                                } else {
     140                                        attributes = _.extend( {
     141                                                id:        file.id,
     142                                                file:      file,
     143                                                uploading: true,
     144                                                date:      new Date(),
     145                                                filename:  file.name
     146                                        }, _.pick( file, 'loaded', 'size', 'percent' ) );
     147
     148                                        file.item = new bp.Models.File( attributes );
     149                                        bp.Uploader.filesQueue.add( file.item );
     150                                }
     151
     152                        } );
     153
     154                        uploader.refresh();
     155                        uploader.start();
     156                } );
     157
     158                /**
     159                 * Update each file item on progress
     160                 *
     161                 * @event UploadProgress
     162                 * @param {plupload.Uploader} uploader Uploader instance.
     163                 * @param {Object}            file
     164                 */
     165                this.uploader.bind( 'UploadProgress', function( uploader, file ) {
     166                        file.item.set( _.pick( file, 'loaded', 'percent' ) );
     167                } );
     168
     169                /**
     170                 * After a file is successfully uploaded, update its model.
     171                 *
     172                 * @event FileUploaded
     173                 * @param {plupload.Uploader} uploader Uploader instance.
     174                 * @param {plupload.File}     file     File that was uploaded.
     175                 * @param {Object}            response Object with response properties.
     176                 * @return {mixed}
     177                 */
     178                this.uploader.bind( 'FileUploaded', function( uploader, file, response ) {
     179                        var message = self.strings.default_error;
     180
     181                        try {
     182                                response = JSON.parse( response.response );
     183                        } catch ( e ) {
     184                                return self.feedback( message, e, file );
     185                        }
     186
     187                        if ( ! _.isObject( response ) || _.isUndefined( response.success ) ) {
     188                                return self.feedback( message, null, file );
     189                        } else if ( ! response.success ) {
     190                                if ( response.data && response.data.message ) {
     191                                        message = response.data.message;
     192                                }
     193
     194                                return self.feedback( message, response.data, file );
     195                        }
     196
     197                        _.each(['file','loaded','size','percent'], function( key ) {
     198                                file.item.unset( key );
     199                        } );
     200
     201                        file.item.set( _.extend( response.data, { uploading: false } ) );
     202
     203                        //  Add the file to the Uploaded ones
     204                        bp.Uploader.filesUploaded.add( file.item );
     205
     206                } );
     207
     208                /**
     209                 * Trigger an event to inform a new upload is being processed
     210                 *
     211                 * Mainly used to remove an eventual warning
     212                 *
     213                 * @event BeforeUpload
     214                 * @param {plupload.Uploader} uploader Uploader instance.
     215                 * @param {Array}             files    Array of file objects that were added to queue by the user.
     216                 */
     217                this.uploader.bind( 'BeforeUpload', function( uploader, files ) {
     218                        $( self ).trigger( 'bp-uploader-new-upload', uploader, files );
     219                } );
     220
     221                /**
     222                 * Reset the filesQueue once the upload is complete
     223                 *
     224                 * @event BeforeUpload
     225                 * @param {plupload.Uploader} uploader Uploader instance.
     226                 * @param {Array}             files    Array of file objects that were added to queue by the user.
     227                 */
     228                this.uploader.bind( 'UploadComplete', function( uploader, files ) {
     229                        $( self ).trigger( 'bp-uploader-upload-complete', uploader, files );
     230                        bp.Uploader.filesQueue.reset();
     231                } );
     232
     233                /**
     234                 * Map Plupload errors & Create a warning when plupload failed
     235                 *
     236                 * @event Error
     237                 * @param {plupload.Uploader} uploader Uploader instance.
     238                 * @param {Object}            pluploadError Plupload error
     239                 */
     240                this.uploader.bind( 'Error', function( uploader, pluploadError ) {
     241                        var message = self.strings.default_error,
     242                                key,
     243                                errors = {
     244                                        'FAILED':                 self.strings.upload_failed,
     245                                        'FILE_EXTENSION_ERROR':   self.strings.invalid_filetype,
     246                                        'IMAGE_FORMAT_ERROR':     self.strings.not_an_image,
     247                                        'IMAGE_MEMORY_ERROR':     self.strings.image_memory_exceeded,
     248                                        'IMAGE_DIMENSIONS_ERROR': self.strings.image_dimensions_exceeded,
     249                                        'GENERIC_ERROR':          self.strings.upload_failed,
     250                                        'IO_ERROR':               self.strings.io_error,
     251                                        'HTTP_ERROR':             self.strings.http_error,
     252                                        'SECURITY_ERROR':         self.strings.security_error,
     253                                        'FILE_SIZE_ERROR':        self.strings.file_exceeds_size_limit.replace( '%s' , pluploadError.file.name )
     254                                };
     255
     256                        // Check for plupload errors.
     257                        for ( key in errors ) {
     258                                if ( pluploadError.code === plupload[ key ] ) {
     259                                        message = errors[ key ];
     260                                        break;
     261                                }
     262                        }
     263
     264                        $( self ).trigger( 'bp-uploader-warning', message );
     265                        uploader.refresh();
     266                } );
     267        };
     268
     269        // Create a very generic Model for files
     270        bp.Models.File = Backbone.Model.extend( {
     271                file: {}
     272        } );
     273
     274        // Add Collections to store queue, uploaded files and errors
     275        $.extend( bp.Uploader, {
     276                filesQueue    : new Backbone.Collection(),
     277                filesUploaded : new Backbone.Collection(),
     278                filesError    : new Backbone.Collection()
     279        } );
     280
     281        // Extend wp.Backbone.View with .prepare() and .inject()
     282        bp.View = bp.Backbone.View.extend( {
     283                inject: function( selector ) {
     284                        this.render();
     285                        $(selector).html( this.el );
     286                        this.views.ready();
     287                },
     288
     289                prepare: function() {
     290                        if ( ! _.isUndefined( this.model ) && _.isFunction( this.model.toJSON ) ) {
     291                                return this.model.toJSON();
     292                        } else {
     293                                return {};
     294                        }
     295                }
     296        } );
     297
     298        // BuddyPress Uploader main view
     299        bp.Views.Uploader = bp.View.extend( {
     300                className: 'bp-uploader-window',
     301                template: bp.template( 'upload-window' ),
     302
     303                defaults: _.pick( BP_Uploader.settings.defaults, 'container', 'drop_element', 'browse_button' ),
     304
     305                initialize: function() {
     306                        this.warning = null;
     307                        this.model = new Backbone.Model( this.defaults );
     308                        this.on( 'ready', this.initUploader );
     309                },
     310
     311                initUploader: function() {
     312                        this.uploader = new bp.Uploader.uploader();
     313                        $( this.uploader ).on( 'bp-uploader-warning', _.bind( this.setWarning, this ) );
     314                        $( this.uploader ).on( 'bp-uploader-new-upload', _.bind( this.resetWarning, this ) );
     315                },
     316
     317                setWarning: function( event, message ) {
     318                        if ( _.isUndefined( message ) ) {
     319                                return;
     320                        }
     321
     322                        this.warning = new bp.Views.uploaderWarning( {
     323                                value: message
     324                        } ).render();
     325
     326                        this.$el.after( this.warning.el );
     327                },
     328
     329                resetWarning: function() {
     330                        if ( _.isNull( this.warning ) ) {
     331                                return;
     332                        }
     333
     334                        this.warning.remove();
     335                        this.warning = null;
     336                }
     337        } );
     338
     339        // BuddyPress Uploader warning view
     340        bp.Views.uploaderWarning = bp.View.extend( {
     341                tagName: 'p',
     342                className: 'warning',
     343                id: 'bp-uploader-warning',
     344
     345                initialize: function() {
     346                        this.value = this.options.value;
     347                },
     348
     349                render: function() {
     350                        this.$el.html( this.value );
     351                        return this;
     352                }
     353        } );
     354
     355        // BuddyPress Uploader Files view
     356        bp.Views.uploaderStatus = bp.View.extend( {
     357                className: 'files',
     358
     359                initialize: function() {
     360                        _.each( this.collection.models, this.addFile, this );
     361                        this.collection.on( 'change:percent', this.progress, this );
     362                        bp.Uploader.filesError.on( 'add', this.feedback, this );
     363                },
     364
     365                addFile: function( file ) {
     366                        this.views.add( new bp.Views.uploaderProgress( { model: file } ) );
     367                },
     368
     369                progress:function( model ) {
     370                        if ( ! _.isUndefined( model.get( 'percent' ) ) ) {
     371                                $( '#' + model.get('id') + ' .progress .bar' ).css( 'width', model.get('percent') + '%' );
     372                        }
     373                },
     374
     375                feedback: function( model ) {
     376                        if ( ! _.isUndefined( model.get( 'message' ) ) && ! _.isUndefined( model.get( 'file' ) ) ) {
     377                                $( '#' + model.get( 'file' ).id ).html( model.get( 'message' ) ).addClass( 'error' );
     378                        }
     379                }
     380        } );
     381
     382        // BuddyPress Uploader File progress view
     383        bp.Views.uploaderProgress = bp.View.extend( {
     384                className: 'bp-uploader-progress',
     385                template: bp.template( 'progress-window' )
     386        } );
     387
     388})( bp, jQuery );
  • src/bp-core/js/webcam.js

    diff --git src/bp-core/js/webcam.js src/bp-core/js/webcam.js
    index e69de29..3d5f1e7 100644
     
     1/* globals bp, BP_Uploader, _, Backbone */
     2
     3window.bp = window.bp || {};
     4
     5( function() {
     6
     7        // Bail if not set
     8        if ( typeof BP_Uploader === 'undefined' ) {
     9                return;
     10        }
     11
     12        bp.Models      = bp.Models || {};
     13        bp.Collections = bp.Collections || {};
     14        bp.Views       = bp.Views || {};
     15
     16        bp.WebCam = {
     17                start: function() {
     18                        this.params = {
     19                                video:          null,
     20                                videoStream:    null,
     21                                capture_enable: false,
     22                                capture:        null,
     23                                canvas:         null,
     24                                warning:        null
     25                        };
     26
     27                        bp.Avatar.nav.on( 'bp-avatar-view:changed', _.bind( this.setView, this ) );
     28                },
     29
     30                setView: function( view ) {
     31                        if ( 'camera' !== view ) {
     32                                // Stop the camera if needed
     33                                if ( ! _.isNull( this.params.video ) ) {
     34                                        this.stop();
     35
     36                                        // Remove all warnings as we're changing the view
     37                                        this.removeWarning();
     38                                }
     39
     40                                // Stop as this is not Camera area
     41                                return;
     42                        }
     43
     44                        // Create the WebCam view
     45                        var cameraView = new bp.Views.WebCamAvatar( { model: new Backbone.Model( { user_media: false } ) } );
     46
     47                        // Add it to views
     48                        bp.Avatar.views.add( { id: 'camera', view: cameraView } );
     49
     50                        // Display it
     51                cameraView.inject( '.bp-avatar' );
     52                },
     53
     54                removeView: function() {
     55                        var camera;
     56
     57                        if ( ! _.isUndefined( bp.Avatar.views.get( 'camera' ) ) ) {
     58                                camera = bp.Avatar.views.get( 'camera' );
     59                                camera.get( 'view' ).remove();
     60                                bp.Avatar.views.remove( { id: 'camera', view: camera } );
     61                        }
     62                },
     63
     64                gotStream: function( stream ) {
     65                        var video = bp.WebCam.params.video;
     66                        bp.WebCam.params.videoStream = stream;
     67
     68                        // User Feedback
     69                        bp.WebCam.displayWarning( 'loaded' );
     70
     71                        video.onerror = function () {
     72                                // User Feedback
     73                                bp.WebCam.displayWarning( 'videoerror' );
     74
     75                                if ( video ) {
     76                                        bp.WebCam.stop();
     77                                }
     78                        };
     79
     80                        stream.onended = bp.WebCam.noStream();
     81
     82                        if ( video.mozSrcObject !== undefined ) {
     83                                video.mozSrcObject = stream;
     84                                video.play();
     85                        } else if ( navigator.mozGetUserMedia ) {
     86                                video.src = stream;
     87                                video.play();
     88                        } else if ( window.URL ) {
     89                                video.src = window.URL.createObjectURL( stream );
     90                        } else {
     91                                video.src = stream;
     92                        }
     93
     94                        bp.WebCam.params.capture_enable = true;
     95                },
     96
     97                stop: function() {
     98                        bp.WebCam.params.capture_enable = false;
     99                        if ( bp.WebCam.params.videoStream ) {
     100                                if ( bp.WebCam.params.videoStream.stop ) {
     101                                        bp.WebCam.params.videoStream.stop();
     102                                } else if ( bp.WebCam.params.videoStream.msStop ) {
     103                                        bp.WebCam.params.videoStream.msStop();
     104                                }
     105                                bp.WebCam.params.videoStream.onended = null;
     106                                bp.WebCam.params.videoStream = null;
     107                        }
     108                        if ( bp.WebCam.params.video ) {
     109                                bp.WebCam.params.video.onerror = null;
     110                                bp.WebCam.params.video.pause();
     111                                if ( bp.WebCam.params.video.mozSrcObject ) {
     112                                        bp.WebCam.params.video.mozSrcObject = null;
     113                                }
     114                                bp.WebCam.params.video.src = '';
     115                        }
     116                },
     117
     118                noStream: function() {
     119                        if ( _.isNull( bp.WebCam.params.videoStream ) ) {
     120                                // User Feedback
     121                                bp.WebCam.displayWarning( 'noaccess' );
     122
     123                                bp.WebCam.removeView();
     124                        }
     125                },
     126
     127                setAvatar: function( avatar ) {
     128                        if ( ! avatar.get( 'url' ) ) {
     129                                bp.WebCam.displayWarning( 'nocapture' );
     130                        }
     131
     132                        // Remove the view
     133                        bp.WebCam.removeView();
     134
     135                        bp.Avatar.setAvatar( avatar );
     136                },
     137
     138                removeWarning: function() {
     139                        if ( ! _.isNull( this.params.warning ) ) {
     140                                this.params.warning.remove();
     141                        }
     142                },
     143
     144                displayWarning: function( code ) {
     145                        this.removeWarning();
     146
     147                        this.params.warning = new bp.Views.uploaderWarning( {
     148                                value: BP_Uploader.strings.camera_warnings[code]
     149                        } );
     150
     151                        this.params.warning.inject( '.bp-avatar-status' );
     152                }
     153        };
     154
     155        // BuddyPress WebCam view
     156        bp.Views.WebCamAvatar = bp.View.extend( {
     157                tagName: 'div',
     158                id: 'bp-webcam-avatar',
     159                template: bp.template( 'bp-avatar-webcam' ),
     160
     161                events: {
     162                        'click .avatar-webcam-capture': 'captureStream',
     163                        'click .avatar-webcam-save': 'saveCapture'
     164                },
     165
     166                initialize: function() {
     167                        var params;
     168
     169                        if ( navigator.getUserMedia || navigator.oGetUserMedia || navigator.mozGetUserMedia || navigator.webkitGetUserMedia || navigator.msGetUserMedia ) {
     170
     171                                // We need to add some cropping stuff to use bp.Avatar.setAvatar()
     172                                params = _.extend( _.pick( BP_Uploader.settings.defaults.multipart_params.bp_params,
     173                                        'object',
     174                                        'item_id',
     175                                        'nonces'
     176                                        ), {
     177                                                user_media:  true,
     178                                                w: BP_Uploader.settings.crop.full_w,
     179                                                h: BP_Uploader.settings.crop.full_h,
     180                                                x: 0,
     181                                                y: 0,
     182                                                type: 'camera'
     183                                        }
     184                                );
     185
     186                                this.model.set( params );
     187                        }
     188
     189                        this.on( 'ready', this.useStream, this );
     190                },
     191
     192                useStream:function() {
     193                        // No support for user media... Stop!
     194                        if ( ! this.model.get( 'user_media' ) ) {
     195                                return;
     196                        }
     197
     198                        this.options.video = new bp.Views.WebCamVideo();
     199                        this.options.canvas = new bp.Views.WebCamCanvas();
     200
     201                        this.$el.find( '#avatar-to-crop' ).append( this.options.video.el );
     202                        this.$el.find( '#avatar-crop-pane' ).append( this.options.canvas.el );
     203
     204                        bp.WebCam.params.video = this.options.video.el;
     205                        bp.WebCam.params.canvas = this.options.canvas.el;
     206
     207                        // User Feedback
     208                        bp.WebCam.displayWarning( 'requesting' );
     209
     210                        if ( navigator.getUserMedia ) {
     211                                navigator.getUserMedia( { video:true }, bp.WebCam.gotStream, bp.WebCams.noStream );
     212                        }  else if ( navigator.oGetUserMedia ) {
     213                                navigator.oGetUserMedia( { video:true }, bp.WebCam.gotStream, bp.WebCam.noStream );
     214                        } else if ( navigator.mozGetUserMedia ) {
     215                                navigator.mozGetUserMedia( { video:true }, bp.WebCam.gotStream, bp.WebCam.noStream );
     216                        } else if ( navigator.webkitGetUserMedia ) {
     217                                navigator.webkitGetUserMedia( { video:true }, bp.WebCam.gotStream, bp.WebCam.noStream );
     218                        } else if (navigator.msGetUserMedia) {
     219                                navigator.msGetUserMedia( { video:true, audio:false }, bp.WebCams.gotStream, bp.WebCam.noStream );
     220                        } else {
     221                                // User Feedback
     222                                bp.WebCam.displayWarning( 'errormsg' );
     223                        }
     224                },
     225
     226                captureStream: function( event ) {
     227                        var sx, sc;
     228                        event.preventDefault();
     229
     230                        if ( ! bp.WebCam.params.capture_enable ) {
     231                                // User Feedback
     232                                bp.WebCam.displayWarning( 'loading' );
     233                                return;
     234                        }
     235
     236                        if ( this.model.get( 'h' ) > this.options.video.el.videoHeight || this.model.get( 'w' ) > this.options.video.el.videoWidth ) {
     237                                bp.WebCam.displayWarning( 'videoerror' );
     238                                return;
     239                        }
     240
     241                        // Set the offset
     242                        sc = this.options.video.el.videoHeight;
     243                        sx = ( this.options.video.el.videoWidth - sc ) / 2;
     244
     245                        this.options.canvas.el.getContext( '2d' ).drawImage( this.options.video.el, sx, 0, sc, sc, 0, 0, this.model.get( 'w' ), this.model.get( 'h' ) );
     246                        bp.WebCam.params.capture = this.options.canvas.el.toDataURL( 'image/png' );
     247                        this.model.set( 'url', bp.WebCam.params.capture );
     248
     249                        // User Feedback
     250                        bp.WebCam.displayWarning( 'ready' );
     251                },
     252
     253                saveCapture: function( event ) {
     254                        event.preventDefault();
     255
     256                        if ( ! bp.WebCam.params.capture ) {
     257                                // User Feedback
     258                                bp.WebCam.displayWarning( 'nocapture' );
     259                                return;
     260                        }
     261
     262                        bp.WebCam.stop();
     263                        bp.WebCam.setAvatar( this.model );
     264                }
     265        } );
     266
     267        // BuddyPress Video stream view
     268        bp.Views.WebCamVideo = bp.View.extend( {
     269                tagName: 'video',
     270                id: 'bp-webcam-video',
     271                attributes: {
     272                        autoplay: 'autoplay'
     273                }
     274        } );
     275
     276        // BuddyPress Canvas (capture) view
     277        bp.Views.WebCamCanvas = bp.View.extend( {
     278                tagName: 'canvas',
     279                id: 'bp-webcam-canvas',
     280                attributes: {
     281                        width:  150,
     282                        height: 150
     283                },
     284
     285                initialize: function() {
     286                        // Make sure to take in account bp_core_avatar_full_height or bp_core_avatar_full_width php filters
     287                        if ( ! _.isUndefined( BP_Uploader.settings.crop.full_h ) && ! _.isUndefined( BP_Uploader.settings.crop.full_w ) ) {
     288                                this.el.attributes.width.value  = BP_Uploader.settings.crop.full_w;
     289                                this.el.attributes.height.value = BP_Uploader.settings.crop.full_h;
     290                        }
     291                }
     292        } );
     293
     294        bp.WebCam.start();
     295
     296})( bp, jQuery );
  • src/bp-loader.php

    diff --git src/bp-loader.php src/bp-loader.php
    index a4795df..86e7a11 100644
    class BuddyPress { 
    437437                require( $this->plugin_dir . 'bp-core/bp-core-theme-compatibility.php' );
    438438
    439439                // Require all of the BuddyPress core libraries
    440                 require( $this->plugin_dir . 'bp-core/bp-core-dependency.php' );
    441                 require( $this->plugin_dir . 'bp-core/bp-core-actions.php'    );
    442                 require( $this->plugin_dir . 'bp-core/bp-core-caps.php'       );
    443                 require( $this->plugin_dir . 'bp-core/bp-core-cache.php'      );
    444                 require( $this->plugin_dir . 'bp-core/bp-core-cssjs.php'      );
    445                 require( $this->plugin_dir . 'bp-core/bp-core-update.php'     );
    446                 require( $this->plugin_dir . 'bp-core/bp-core-options.php'    );
    447                 require( $this->plugin_dir . 'bp-core/bp-core-classes.php'    );
    448                 require( $this->plugin_dir . 'bp-core/bp-core-taxonomy.php'   );
    449                 require( $this->plugin_dir . 'bp-core/bp-core-filters.php'    );
    450                 require( $this->plugin_dir . 'bp-core/bp-core-avatars.php'    );
    451                 require( $this->plugin_dir . 'bp-core/bp-core-widgets.php'    );
    452                 require( $this->plugin_dir . 'bp-core/bp-core-template.php'   );
    453                 require( $this->plugin_dir . 'bp-core/bp-core-adminbar.php'   );
    454                 require( $this->plugin_dir . 'bp-core/bp-core-buddybar.php'   );
    455                 require( $this->plugin_dir . 'bp-core/bp-core-catchuri.php'   );
    456                 require( $this->plugin_dir . 'bp-core/bp-core-component.php'  );
    457                 require( $this->plugin_dir . 'bp-core/bp-core-functions.php'  );
    458                 require( $this->plugin_dir . 'bp-core/bp-core-moderation.php' );
    459                 require( $this->plugin_dir . 'bp-core/bp-core-loader.php'     );
     440                require( $this->plugin_dir . 'bp-core/bp-core-dependency.php'  );
     441                require( $this->plugin_dir . 'bp-core/bp-core-actions.php'     );
     442                require( $this->plugin_dir . 'bp-core/bp-core-caps.php'        );
     443                require( $this->plugin_dir . 'bp-core/bp-core-cache.php'       );
     444                require( $this->plugin_dir . 'bp-core/bp-core-cssjs.php'       );
     445                require( $this->plugin_dir . 'bp-core/bp-core-update.php'      );
     446                require( $this->plugin_dir . 'bp-core/bp-core-options.php'     );
     447                require( $this->plugin_dir . 'bp-core/bp-core-classes.php'     );
     448                require( $this->plugin_dir . 'bp-core/bp-core-taxonomy.php'    );
     449                require( $this->plugin_dir . 'bp-core/bp-core-filters.php'     );
     450                require( $this->plugin_dir . 'bp-core/bp-core-attachments.php' );
     451                require( $this->plugin_dir . 'bp-core/bp-core-avatars.php'     );
     452                require( $this->plugin_dir . 'bp-core/bp-core-widgets.php'     );
     453                require( $this->plugin_dir . 'bp-core/bp-core-template.php'    );
     454                require( $this->plugin_dir . 'bp-core/bp-core-adminbar.php'    );
     455                require( $this->plugin_dir . 'bp-core/bp-core-buddybar.php'    );
     456                require( $this->plugin_dir . 'bp-core/bp-core-catchuri.php'    );
     457                require( $this->plugin_dir . 'bp-core/bp-core-component.php'   );
     458                require( $this->plugin_dir . 'bp-core/bp-core-functions.php'   );
     459                require( $this->plugin_dir . 'bp-core/bp-core-moderation.php'  );
     460                require( $this->plugin_dir . 'bp-core/bp-core-loader.php'      );
    460461
    461462                // Skip or load deprecated content
    462463                if ( false !== $this->load_deprecated ) {
  • src/bp-members/admin/css/admin.css

    diff --git src/bp-members/admin/css/admin.css src/bp-members/admin/css/admin.css
    index e4599f7..c780cf9 100644
    div#community-profile-page li.bp-members-profile-stats:before, 
    2525div#community-profile-page li.bp-friends-profile-stats:before,
    2626div#community-profile-page li.bp-groups-profile-stats:before,
    2727div#community-profile-page li.bp-blogs-profile-stats:before,
    28 div#community-profile-page a.bp-xprofile-avatar-user-admin:before {
     28div#community-profile-page a.bp-xprofile-avatar-user-admin:before,
     29div#community-profile-page a.bp-xprofile-avatar-user-edit:before {
    2930        font: normal 20px/1 'dashicons';
    3031        speak: none;
    3132        display: inline-block;
    div#community-profile-page a.bp-xprofile-avatar-user-admin:before { 
    6061        content:"\f182";
    6162}
    6263
     64div#community-profile-page a.bp-xprofile-avatar-user-edit:before {
     65        content: "\f107";
     66}
     67
    6368div#community-profile-page div#bp_xprofile_user_admin_avatar div.avatar {
    6469        width:150px;
    6570        margin:0 auto;
  • src/bp-members/bp-members-admin.php

    diff --git src/bp-members/bp-members-admin.php src/bp-members/bp-members-admin.php
    index 1226659..fbf807d 100644
    class BP_Members_Admin { 
    217217         * @return int
    218218         */
    219219        private function get_user_id() {
    220                 $user_id = get_current_user_id();
     220                if ( ! empty( $this->user_id ) ) {
     221                        return $this->user_id;
     222                }
     223
     224                $this->user_id = (int) get_current_user_id();
    221225
    222                 // We'll need a user ID when not on the user admin
     226                // We'll need a user ID when not on self profile
    223227                if ( ! empty( $_GET['user_id'] ) ) {
    224                         $user_id = $_GET['user_id'];
     228                        $this->user_id = (int) $_GET['user_id'];
    225229                }
    226230
    227                 return intval( $user_id );
     231                return $this->user_id;
    228232        }
    229233
    230234        /**
  • src/bp-templates/bp-legacy/buddypress/assets/_attachments/avatars/camera.php

    diff --git src/bp-templates/bp-legacy/buddypress/assets/_attachments/avatars/camera.php src/bp-templates/bp-legacy/buddypress/assets/_attachments/avatars/camera.php
    index e69de29..267d433 100644
     
     1<?php
     2/**
     3 * BuddyPress Avatars camera template
     4 *
     5 * This template is used to create the camera Backbone views
     6 *
     7 * @since 2.3
     8 *
     9 * @package BuddyPress
     10 * @subpackage bp-attachments
     11 */
     12?>
     13<script id="tmpl-bp-avatar-webcam" type="text/html">
     14        <# if ( ! data.user_media ) { #>
     15                <div id="bp-webcam-message">
     16                        <p class="warning"><?php esc_html_e( 'Your browser does not support the camera feature', 'buddypress' );?></p>
     17                </div>
     18        <# } else { #>
     19                <div id="avatar-to-crop"></div>
     20                <div id="avatar-crop-pane" class="avatar" style="width:{{data.w}}px; height:{{data.h}}px"></div>
     21                <div id="avatar-crop-actions">
     22                        <a class="button avatar-webcam-capture" href="#"><?php esc_html_e( 'Capture', 'buddypress' );?></a>
     23                        <a class="button avatar-webcam-save hide" href="#"><?php esc_html_e( 'Save', 'buddypress' );?></a>
     24                </div>
     25        <# } #>
     26</script>
  • src/bp-templates/bp-legacy/buddypress/assets/_attachments/avatars/crop.php

    diff --git src/bp-templates/bp-legacy/buddypress/assets/_attachments/avatars/crop.php src/bp-templates/bp-legacy/buddypress/assets/_attachments/avatars/crop.php
    index e69de29..086632c 100644
     
     1<?php
     2/**
     3 * BuddyPress Avatars crop template
     4 *
     5 * This template is used to create the crop Backbone views
     6 *
     7 * @since 2.3
     8 *
     9 * @package BuddyPress
     10 * @subpackage bp-attachments
     11 */
     12?>
     13<script id="tmpl-bp-avatar-item" type="text/html">
     14        <div id="avatar-to-crop">
     15                <img src="{{data.url}}"/>
     16        </div>
     17        <div id="avatar-crop-pane" class="avatar" style="width:{{data.full_w}}px; height:{{data.full_h}}px">
     18                <img src="{{data.url}}" id="avatar-crop-preview"/>
     19        </div>
     20        <div id="avatar-crop-actions">
     21                <a class="button avatar-crop-submit" href="#"><?php esc_html_e( 'Crop Image', 'buddypress' ); ?></a>
     22        </div>
     23</script>
  • src/bp-templates/bp-legacy/buddypress/assets/_attachments/avatars/index.php

    diff --git src/bp-templates/bp-legacy/buddypress/assets/_attachments/avatars/index.php src/bp-templates/bp-legacy/buddypress/assets/_attachments/avatars/index.php
    index e69de29..82c401f 100644
     
     1<?php
     2/**
     3 * BuddyPress Avatars main template
     4 *
     5 * This template is used to inject the BuddyPress Backbone views
     6 * dealing with avatars.
     7 * It's also used to create the common Backbone views
     8 *
     9 * @since 2.3
     10 *
     11 * @package BuddyPress
     12 * @subpackage bp-attachments
     13 */
     14
     15
     16/**
     17 * This action is for internal use, please do not use it
     18 */
     19do_action( 'bp_attachments_avatar_check_template' );
     20?>
     21<div class="bp-avatar-nav"></div>
     22<div class="bp-avatar"></div>
     23<div class="bp-avatar-status"></div>
     24
     25<script type="text/html" id="tmpl-bp-avatar-nav">
     26        <a href="{{data.href}}" class="bp-avatar-nav-item" data-nav="{{data.id}}">{{data.name}}</a>
     27</script>
     28
     29<?php bp_attachments_get_template_part( 'uploader' ); ?>
     30
     31<?php bp_attachments_get_template_part( 'avatars/crop' ); ?>
     32
     33<?php bp_attachments_get_template_part( 'avatars/camera' ); ?>
     34
     35<script id="tmpl-bp-avatar-delete" type="text/html">
     36        <# if ( 'user' === data.object ) { #>
     37                <p><?php _e( "If you'd like to delete your current profile photo but not upload a new one, please use the delete profile photo button.", 'buddypress' ); ?></p>
     38                <p><a class="button edit" id="bp-delete-avatar" href="#" title="<?php esc_attr_e( 'Delete Profile Photo', 'buddypress' ); ?>"><?php esc_html_e( 'Delete My Profile Photo', 'buddypress' ); ?></a></p>
     39        <# } else if ( 'group' === data.object ) { #>
     40                <p><?php _e( "If you'd like to remove the existing group profile photo but not upload a new one, please use the delete group profile photo button.", 'buddypress' ); ?></p>
     41                <p><a class="button edit" id="bp-delete-avatar" href="#" title="<?php esc_attr_e( 'Delete Group Profile Photo', 'buddypress' ); ?>"><?php esc_html_e( 'Delete Group Profile Photo', 'buddypress' ); ?></a></p>
     42        <# } else { #>
     43                <?php do_action( 'bp_attachments_avatar_delete_template' ); ?>
     44        <# } #>
     45</script>
     46
     47<?php do_action( 'bp_attachments_avatar_main_template' ); ?>
  • src/bp-templates/bp-legacy/buddypress/assets/_attachments/uploader.php

    diff --git src/bp-templates/bp-legacy/buddypress/assets/_attachments/uploader.php src/bp-templates/bp-legacy/buddypress/assets/_attachments/uploader.php
    index e69de29..ebbc90f 100644
     
     1<?php
     2/**
     3 * BuddyPress Uploader templates
     4 *
     5 * This template is used to create the BuddyPress Uploader Backbone views
     6 *
     7 * @since 2.3
     8 *
     9 * @package BuddyPress
     10 * @subpackage bp-attachments
     11 */
     12?>
     13<script type="text/html" id="tmpl-upload-window">
     14        <?php if ( ! _device_can_upload() ) : ?>
     15                <h3 class="upload-instructions"><?php esc_html_e( 'The web browser on your device cannot be used to upload files.', 'buddypress' ); ?></h3>
     16        <?php elseif ( is_multisite() && ! is_upload_space_available() ) : ?>
     17                <h3 class="upload-instructions"><?php esc_html_e( 'Upload Limit Exceeded', 'buddypress' ); ?></h3>
     18        <?php else : ?>
     19                <div id="{{data.container}}">
     20                        <div id="{{data.drop_element}}">
     21                                <div class="drag-drop-inside">
     22                                        <p class="drag-drop-info"><?php esc_html_e( 'Drop your file here', 'buddypress' ); ?></p>
     23                                        <p><?php _ex( 'or', 'Uploader: Drop your file here - or - Select your File', 'buddypress' ); ?></p>
     24                                        <p class="drag-drop-buttons"><input id="{{data.browse_button}}" type="button" value="<?php esc_attr_e( 'Select your File', 'buddypress' ); ?>" class="button" /></p>
     25                                </div>
     26                        </div>
     27                </div>
     28        <?php endif; ?>
     29</script>
     30
     31<script type="text/html" id="tmpl-progress-window">
     32        <div id="{{data.id}}">
     33                <div class="progress">
     34                        <div class="bar"></div>
     35                </div>
     36                <div class="filename">{{data.filename}}</div>
     37        </div>
     38</script>
  • src/bp-templates/bp-legacy/buddypress/groups/create.php

    diff --git src/bp-templates/bp-legacy/buddypress/groups/create.php src/bp-templates/bp-legacy/buddypress/groups/create.php
    index 5741f10..fd2f31e 100644
     
    6060
    6161
    6262                                        <label>
    63                                                 <input type="radio" name="group-status" value="private"<?php if ( 'private' == bp_get_new_group_status() ) { ?> checked="checked"<?php } ?> /> 
     63                                                <input type="radio" name="group-status" value="private"<?php if ( 'private' == bp_get_new_group_status() ) { ?> checked="checked"<?php } ?> />
    6464                                                <strong><?php _e( 'This is a private group', 'buddypress' ); ?></strong>
    6565                                        </label>
    6666                                        <ul>
     
    7171
    7272
    7373                                        <label>
    74                                                 <input type="radio" name="group-status" value="hidden"<?php if ( 'hidden' == bp_get_new_group_status() ) { ?> checked="checked"<?php } ?> /> 
     74                                                <input type="radio" name="group-status" value="hidden"<?php if ( 'hidden' == bp_get_new_group_status() ) { ?> checked="checked"<?php } ?> />
    7575                                                <strong><?php _e('This is a hidden group', 'buddypress' ); ?></strong>
    7676                                        </label>
    7777                                        <ul>
     
    153153                                                <p><?php _e( 'To skip the group profile photo upload process, hit the "Next Step" button.', 'buddypress' ); ?></p>
    154154                                        </div><!-- .main-column -->
    155155
     156                                        <?php
     157                                        /**
     158                                         * Load the Avatar UI templates
     159                                         *
     160                                         * @since  BuddyPress (2.3.0)
     161                                         */
     162                                        bp_avatar_get_templates(); ?>
     163
    156164                                <?php endif; ?>
    157165
    158166                                <?php if ( 'crop-image' == bp_get_avatar_admin_step() ) : ?>
     
    306314
    307315</div>
    308316
    309 <?php do_action( 'bp_after_create_group_page' ); ?>
    310  No newline at end of file
     317<?php do_action( 'bp_after_create_group_page' ); ?>
  • src/bp-templates/bp-legacy/buddypress/groups/single/admin.php

    diff --git src/bp-templates/bp-legacy/buddypress/groups/single/admin.php src/bp-templates/bp-legacy/buddypress/groups/single/admin.php
    index a5811ff..316f9f7 100644
     
    140140
    141141                        <?php endif; ?>
    142142
     143                        <?php
     144                        /**
     145                         * Load the Avatar UI templates
     146                         *
     147                         * @since  BuddyPress (2.3.0)
     148                         */
     149                        bp_avatar_get_templates(); ?>
     150
    143151                        <?php wp_nonce_field( 'bp_avatar_upload' ); ?>
    144152
    145153        <?php endif; ?>
  • src/bp-templates/bp-legacy/buddypress/members/single/profile/change-avatar.php

    diff --git src/bp-templates/bp-legacy/buddypress/members/single/profile/change-avatar.php src/bp-templates/bp-legacy/buddypress/members/single/profile/change-avatar.php
    index f2a3060..71b1563 100644
     
    5050
    5151        </form>
    5252
     53        <?php
     54        /**
     55         * Load the Avatar UI templates
     56         *
     57         * @since  BuddyPress (2.3.0)
     58         */
     59        bp_avatar_get_templates(); ?>
     60
    5361<?php else : ?>
    5462
    5563        <p><?php _e( 'Your profile photo will be used on your profile and throughout the site. To change your profile photo, please create an account with <a href="http://gravatar.com">Gravatar</a> using the same email address as you used to register with this site.', 'buddypress' ); ?></p>
  • src/bp-xprofile/bp-xprofile-admin.php

    diff --git src/bp-xprofile/bp-xprofile-admin.php src/bp-xprofile/bp-xprofile-admin.php
    index 8f2ff63..a816b69 100644
    class BP_XProfile_User_Admin { 
    673673         * @since BuddyPress (2.0.0)
    674674         */
    675675        private function setup_actions() {
     676                // Enqueue scripts
     677                add_action( 'bp_members_admin_enqueue_scripts', array( $this, 'enqueue_scripts' ), 10, 1 );
    676678
    677679                // Register the metabox in Member's community admin profile
    678680                add_action( 'bp_members_admin_xprofile_metabox', array( $this, 'register_metaboxes' ), 10, 3 );
    class BP_XProfile_User_Admin { 
    682684        }
    683685
    684686        /**
     687         * Enqueue needed scripts.
     688         *
     689         * @access public
     690         * @since BuddyPress (2.3.0)
     691         */
     692        public function enqueue_scripts( $screen_id ) {
     693                if ( false === strpos( $screen_id, 'users_page_bp-profile-edit' ) && false === strpos( $screen_id, 'profile_page_bp-profile-edit' ) ) {
     694                        return;
     695                }
     696
     697                /**
     698                 * Get Thickbox
     699                 *
     700                 * We cannot simply use add_thickbox() here as WordPress is not playing
     701                 * nice with Thickbox width/height see https://core.trac.wordpress.org/ticket/17249
     702                 * Using media-upload might be interesting in the future for the send to editor stuff
     703                 * and we make sure the tb_window is wide enougth
     704                 */
     705                wp_enqueue_style ( 'thickbox' );
     706                wp_enqueue_script( 'media-upload' );
     707
     708                // Get Avatar Uploader
     709                bp_attachments_enqueue_scripts( 'BP_Attachment_Avatar' );
     710        }
     711
     712        /**
    685713         * Register the xProfile metabox on Community Profile admin page.
    686714         *
    687715         * @access public
    class BP_XProfile_User_Admin { 
    10391067
    10401068                        <?php endif; ?>
    10411069
     1070                        <?php // Avatar Editor ;?>
     1071                        <a href="#TB_inline?width=800px&height=400px&inlineId=bp-xprofile-avatar-editor" title="<?php esc_attr_e( 'Edit Profile Photo', 'buddypress' );?>" class="thickbox bp-xprofile-avatar-user-edit"><?php esc_html_e( 'Edit Profile Photo', 'buddypress' ); ?></a>
     1072                        <div id="bp-xprofile-avatar-editor" style="display:none;">
     1073                                <?php bp_attachments_get_template_part( 'avatars/index' ); ?>
     1074                        </div>
     1075
    10421076                </div>
    10431077                <?php
    10441078        }