Skip to:
Content

BuddyPress.org

Ticket #6004: 6004.patch

File 6004.patch, 261.3 KB (added by imath, 5 years ago)
  • src/bp-attachments/bp-attachments-actions.php

    diff --git src/bp-attachments/bp-attachments-actions.php src/bp-attachments/bp-attachments-actions.php
    index e69de29..df07840 100644
     
     1<?php
     2/**
     3 * Attachments Actions.
     4 *
     5 * @package BuddyPress
     6 * @subpackage Attachments Actions
     7 */
     8
     9// Exit if accessed directly
     10defined( 'ABSPATH' ) or exit;
     11
     12/**
     13 * This function runs when an action is set for a screen:
     14 * example.com/members/andy/profile/change-avatar/ [delete-avatar]
     15 *
     16 * The function will delete the active avatar for a user.
     17 *
     18 * @since  BuddyPress (2.2.0)
     19 *
     20 * @package BuddyPress
     21 * @subpackage Attachments Actions
     22 *
     23 * @uses   bp_is_user_change_avatar()
     24 * @uses   bp_is_action_variable()
     25 * @uses   bp_is_my_profile()
     26 * @uses   bp_current_user_can()
     27 * @uses   bp_attachments_delete_existing_avatar()
     28 * @uses   bp_displayed_user_id()
     29 * @uses   bp_core_add_message()
     30 * @uses   bp_core_redirect()
     31 */
     32function bp_attachments_action_xprofile_delete_avatar() {
     33
     34        if ( ! bp_is_user_change_avatar() || ! bp_is_action_variable( 'delete-avatar', 0 ) ) {
     35                return false;
     36        }
     37
     38        // Check the nonce
     39        check_admin_referer( 'bp_delete_avatar_link' );
     40
     41        if ( ! bp_is_my_profile() && ! bp_current_user_can( 'bp_moderate' ) ) {
     42                return false;
     43        }
     44
     45        if ( bp_attachments_delete_existing_avatar( array( 'item_id' => bp_displayed_user_id() ) ) ) {
     46                bp_core_add_message( __( 'Your profile photo was deleted successfully!', 'buddypress' ) );
     47        } else {
     48                bp_core_add_message( __( 'There was a problem deleting your profile photo; please try again.', 'buddypress' ), 'error' );
     49        }
     50
     51        bp_core_redirect( wp_get_referer() );
     52}
     53add_action( 'bp_actions', 'bp_attachments_action_xprofile_delete_avatar' );
     54
     55/**
     56 * Handle the display of a new group's Avatar create step.
     57 *
     58 * @since  BuddyPress (2.2.0)
     59 *
     60 * @package BuddyPress
     61 * @subpackage Attachments Actions
     62 *
     63 * @param  int $group_id the new group id
     64 * @uses   bp_is_active()
     65 * @uses   bp_get_groups_current_create_step()
     66 * @uses   buddypress()
     67 * @uses   bp_get_new_group_id()
     68 * @uses   bp_attachments_avatar_handle_upload()
     69 * @uses   is_wp_error()
     70 * @uses   bp_core_add_message()
     71 * @uses   bp_attachments_avatar_handle_crop()
     72 */
     73function bp_attachments_action_group_avatar_create_step( $group_id = 0 ) {
     74        if ( ! bp_is_active( 'groups' ) && 'group-avatar' != bp_get_groups_current_create_step() && ! isset( $_POST['upload'] ) ) {
     75                return;
     76        }
     77
     78        $bp = buddypress();
     79
     80        if ( ! isset( $bp->avatar_admin ) ) {
     81                $bp->avatar_admin = new stdClass();
     82        }
     83
     84        if ( empty( $group_id ) ) {
     85                $group_id = bp_get_new_group_id();
     86        }
     87
     88        if ( ! empty( $_FILES ) && isset( $_POST['upload'] ) ) {
     89
     90                // Pass the file to the avatar upload handler
     91                $uploaded_avatar = bp_attachments_avatar_handle_upload( $_FILES, 'groups', $group_id );
     92
     93                if ( is_wp_error( $uploaded_avatar ) ) {
     94                        bp_core_add_message( $uploaded_avatar->get_error_message(), 'error' );
     95
     96                // Go to crop step
     97                } else {
     98                        // Avoid some error messages to be output if
     99                        // everything is ok.
     100                        $bp->template_message   = false;
     101                        $bp->avatar_admin->step = 'crop-image';
     102
     103                        // Make sure we include the jQuery jCrop file for image cropping
     104                        add_action( 'wp_print_scripts', 'bp_attachments_add_jquery_cropper' );
     105                }
     106        }
     107
     108        // If the image cropping is done, crop the image and save a full/thumb version
     109        if ( isset( $_POST['avatar-crop-submit'] ) && isset( $_POST['upload'] ) ) {
     110
     111                $args = array(
     112                        'object'        => 'group',
     113                        'avatar_dir'    => 'avatar_group_dir',
     114                        'item_id'       => $group_id,
     115                        'original_file' => $_POST['image_src'],
     116                        'crop_x'        => $_POST['x'],
     117                        'crop_y'        => $_POST['y'],
     118                        'crop_w'        => $_POST['w'],
     119                        'crop_h'        => $_POST['h']
     120                );
     121
     122                if ( ! bp_attachments_avatar_handle_crop( $args ) ) {
     123                        bp_core_add_message( __( 'There was an error saving the group profile photo, please try uploading again.', 'buddypress' ), 'error' );
     124                } else {
     125                        bp_core_add_message( __( 'The group profile photo was uploaded successfully!', 'buddypress' ) );
     126                }
     127        }
     128}
     129add_action( 'bp_groups_avatar_create_step', 'bp_attachments_action_group_avatar_create_step', 10, 1 );
     130
     131/**
     132 * Add the specific templates needed by BP Attachments Editor
     133 *
     134 * @since BuddyPress (2.2.0)
     135 */
     136function bp_attachment_load_backbone_tmpl() {
     137        ?>
     138        <script type="text/html" id="tmpl-bp-attachment-details">
     139                <h3>
     140                        <?php _e('Attachment Details'); ?>
     141
     142                        <span class="settings-save-status">
     143                                <span class="spinner"></span>
     144                                <span class="saved"><?php esc_html_e('Saved.'); ?></span>
     145                        </span>
     146                </h3>
     147                <div class="attachment-info">
     148                        <div class="thumbnail">
     149                                <# if ( data.uploading ) { #>
     150                                        <div class="media-progress-bar"><div></div></div>
     151                                <# } else if ( 'image' === data.type ) { #>
     152                                        <img src="{{ data.size.url }}" draggable="false" />
     153                                <# } else { #>
     154                                        <img src="{{ data.icon }}" class="icon" draggable="false" />
     155                                <# } #>
     156                        </div>
     157                        <div class="details">
     158                                <div class="filename">{{ data.filename }}</div>
     159                                <div class="uploaded">{{ data.dateFormatted }}</div>
     160
     161                                <# if ( 'image' === data.type && ! data.uploading ) { #>
     162                                        <# if ( data.width && data.height ) { #>
     163                                                <div class="dimensions">{{ data.width }} &times; {{ data.height }}</div>
     164                                        <# } #>
     165
     166                                        <# if ( data.can.save ) { #>
     167                                                <a class="edit-bp-attachment" href="{{ data.editLink }}"><?php _e( 'Edit Options', 'buddypress' ); ?></a>
     168                                        <# } #>
     169                                <# } #>
     170
     171                                <# if ( data.fileLength ) { #>
     172                                        <div class="file-length"><?php _e( 'Length:' ); ?> {{ data.fileLength }}</div>
     173                                <# } #>
     174
     175                                <# if ( ! data.uploading && data.can.remove ) { #>
     176                                        <?php if ( MEDIA_TRASH ): ?>
     177                                                <a class="trash-attachment" href="#"><?php _e( 'Trash' ); ?></a>
     178                                        <?php else: ?>
     179                                                <a class="delete-attachment" href="#"><?php _e( 'Delete Permanently' ); ?></a>
     180                                        <?php endif; ?>
     181                                <# } #>
     182
     183                                <div class="compat-meta">
     184                                        <# if ( data.compat && data.compat.meta ) { #>
     185                                                {{{ data.compat.meta }}}
     186                                        <# } #>
     187                                </div>
     188                        </div>
     189                </div>
     190
     191                <# var maybeReadOnly = data.can.save || data.allowLocalEdits ? '' : 'readonly'; #>
     192                        <label class="setting" data-setting="title">
     193                                <span><?php _e('Title'); ?></span>
     194                                <input type="text" value="{{ data.title }}" {{ maybeReadOnly }} />
     195                        </label>
     196                        <label class="setting" data-setting="description">
     197                                <span><?php _e('Description'); ?></span>
     198                                <textarea {{ maybeReadOnly }}>{{ data.description }}</textarea>
     199                        </label>
     200        </script>
     201
     202        <script type="text/html" id="tmpl-avatar">
     203                <div class="avatar-preview">
     204                        <# if ( data.uploading ) { #>
     205                                <div class="media-progress-bar"><div></div></div>
     206                        <# } else { #>
     207                        <div class="resized">
     208                                <div class="centered">
     209                                        <img src="{{ data.size.url }}" draggable="false" id="avatar-to-crop" />
     210                                </div>
     211                        </div>
     212                        <# } #>
     213                </div>
     214        </script>
     215
     216        <script type="text/html" id="tmpl-bp-avatar-details">
     217                <h3>
     218                        <?php _e('Avatar preview', 'buddypress'); ?>
     219                </h3>
     220                <div class="attachment-info">
     221                        <div class="avatar-thumb" style="width:<?php echo bp_core_avatar_full_width();?>px;height:<?php echo bp_core_avatar_full_height();?>px;overflow:hidden">
     222                                <img draggable="false" id="avatar-crop-preview" />
     223                        </div>
     224                </div>
     225        </script>
     226
     227        <script type="text/html" id="tmpl-bp-preview">
     228                <# if ( data.id ) { #>
     229                        <div class="centered">
     230                                <img src="{{ data.img }}" style="max-width:100%"/>
     231                        </div>
     232                <# } #>
     233        </script>
     234        <?php
     235}
     236add_action( 'print_media_templates', 'bp_attachment_load_backbone_tmpl' );
     237
     238
     239/**
     240 * Handle uploads if no-js
     241 *
     242 * @since BuddyPress (2.2.0)
     243 */
     244function bp_attachments_catch_upload() {
     245
     246        if ( ! empty( $_POST['bp_attachment_upload'] ) ) {
     247
     248                check_admin_referer( 'bp_attachments_upload', 'bp_attachments_upload_nonce' );
     249
     250                $redirect = $_POST['_wp_http_referer'];
     251
     252                $file_name = 'bp_attachment_file';
     253
     254                if ( ! empty( $_POST['file_data'] ) ) {
     255                        $file_name = $_POST['file_data'];
     256                }
     257
     258                if ( ! empty( $_FILES[ $file_name ] ) ) {
     259
     260                        $args = array();
     261
     262                        if ( ! empty( $_POST['item_id'] ) )
     263                                $args['item_id'] = absint( $_POST['item_id'] );
     264
     265                        if ( ! empty( $_POST['item_type'] ) ) {
     266                                $args['item_type'] = $_POST['item_type'];
     267                        } else {
     268                                $args['item_type'] = 'attachment';
     269                        }
     270
     271                        if ( ! empty( $_POST['component'] ) )
     272                                $args['component'] = $_POST['component'];
     273
     274                        if ( ! empty( $_POST['action'] ) )
     275                                $args['action'] = $_POST['action'];
     276
     277                        // We don't categorized members as a bp_component term
     278                        if ( 'members' == $args['component'] ) {
     279                                unset( $args['component'] );
     280                        }
     281
     282                        $cap_args = false;
     283
     284                        if ( ! empty( $args['component'] ) ) {
     285                                $cap_args = array( 'component' => $args['component'], 'item_id' => $args['item_id'] );
     286                        }
     287
     288                        // capability check
     289                        if ( ! bp_attachments_current_user_can( 'publish_bp_attachments', $cap_args ) ) {
     290                                bp_core_add_message( __( 'Error: you are not allowed to create this attachment.', 'buddypress' ), 'error' );
     291                                bp_core_redirect( $redirect );
     292                        }
     293
     294                        // Dealing with an Avatar
     295                        if ( 'avatar' == $args['item_type'] ) {
     296                                $avatar = bp_attachments_avatar_handle_upload( $_FILES, $args['component'], $args['item_id'] );
     297
     298                                if ( ! is_wp_error( $avatar ) ) {
     299                                        switch ( $component ) {
     300                                                case 'groups' :
     301                                                        $object     = 'group';
     302                                                        $avatar_dir = 'avatar_group_dir';
     303                                                        break;
     304
     305                                                case 'blogs' :
     306                                                        $object     = 'blog';
     307                                                        $avatar_dir = 'avatar_blog_dir';
     308                                                        break;
     309
     310                                                case 'xprofile' :
     311                                                default         :
     312                                                        $object     = 'user';
     313                                                        $avatar_dir = 'avatar_user_dir';
     314                                                        break;
     315                                        }
     316
     317                                        $crop = bp_attachments_avatar_handle_crop( array(
     318                                                'object'        => $object,
     319                                                'avatar_dir'    => $avatar_dir,
     320                                                'item_id'       => $args['item_id'],
     321                                                'original_file' => $avatar,
     322                                        ) );
     323
     324                                        if ( ! empty( $crop ) ) {
     325                                                $attachment_id = $crop;
     326                                        } else {
     327                                                $attachment_id = new WP_Error( 'crop_error', sprintf( __( 'There was an error saving the %s avatar, please try uploading again.', 'buddypress' ), $object ) );
     328                                        }
     329                                } else {
     330                                        $attachment_id = $avatar;
     331                                }
     332                        // Dealing with an Attachment
     333                        } else {
     334                                $attachment_id = bp_attachments_handle_upload( $args );
     335                        }
     336
     337                        if ( is_wp_error( $attachment_id ) ) {
     338                                bp_core_add_message( sprintf( __( 'Error: %s', 'buddypress' ), $attachment_id->get_error_message() ), 'error' );
     339                        } else {
     340                                bp_core_add_message( sprintf( __( '%s successfully uploaded', 'buddypress' ), ucfirst( $args['item_type'] ) ) );
     341                        }
     342
     343                        bp_core_redirect( $redirect );
     344                }
     345        }
     346}
     347add_action( 'bp_actions', 'bp_attachments_catch_upload' );
  • src/bp-attachments/bp-attachments-admin.php

    diff --git src/bp-attachments/bp-attachments-admin.php src/bp-attachments/bp-attachments-admin.php
    index e69de29..7c3f41b 100644
     
     1<?php
     2/**
     3 * Attachments Admin.
     4 *
     5 * A media component, for others !
     6 *
     7 * @package BuddyPress
     8 * @subpackage Attachments Administration
     9 */
     10
     11// Exit if accessed directly
     12defined( 'ABSPATH' ) or exit;
     13
     14if ( ! class_exists( 'BP_Attachment_Admin' ) ) :
     15/**
     16 * Load BP Attachments admin area.
     17 *
     18 * @package BuddyPress
     19 * @subpackage Attachments Administration
     20 *
     21 * @since BuddyPress (2.2.0)
     22 */
     23class BP_Attachments_Admin {
     24        /**
     25         * Setup BP Attachments Admin.
     26         *
     27         * @access public
     28         * @since BuddyPress (2.2.0)
     29         *
     30         * @uses buddypress() to get BuddyPress main instance
     31         */
     32        public static function register_attachments_admin() {
     33                if( ! is_admin() )
     34                        return;
     35
     36                $bp = buddypress();
     37
     38                if( empty( $bp->attachments->admin ) ) {
     39                        $bp->attachments->admin = new self;
     40                }
     41
     42                return $bp->attachments->admin;
     43        }
     44
     45        /**
     46         * Constructor method.
     47         *
     48         * @access public
     49         * @since BuddyPress (2.2.0)
     50         */
     51        public function __construct() {
     52                $this->setup_actions();
     53        }
     54
     55        /**
     56         * Set admin-related actions and filters.
     57         *
     58         * @access private
     59         * @since BuddyPress (2.2.0)
     60         */
     61        private function setup_actions() {
     62                add_action( 'bp_groups_admin_meta_boxes',        array( $this, 'group_avatar' )        );
     63                add_action( 'bp_members_admin_xprofile_metabox', array( $this, 'user_avatar'  ), 10, 2 );
     64        }
     65
     66        /**
     67         * Add a metabox to edit Group's Avatar
     68         *
     69         * @access public
     70         * @since BuddyPress (2.2.0)
     71         */
     72        public function group_avatar() {
     73                add_meta_box(
     74                        'bp_groups_avatar',
     75                        _x( 'Avatar', 'group admin edit screen', 'buddypress' ),
     76                        array( &$this, 'group_avatar_metabox' ),
     77                        get_current_screen()->id,
     78                        'side',
     79                        'low'
     80                );
     81        }
     82
     83        /**
     84         * Displays the metabox to edit Group's Avatar
     85         *
     86         * @access public
     87         * @since BuddyPress (2.2.0)
     88         */
     89        public function group_avatar_metabox( $item = null ) {
     90                if ( empty( $item ) )
     91                        return;
     92                ?>
     93                <div id="groups-avatar">
     94                <?php
     95                // Displays the current avatar and a link to delete it.
     96                if ( bp_get_group_has_avatar( $item->id ) ) {
     97                        echo bp_core_fetch_avatar( array( 'item_id' => $item->id, 'object' => 'group', 'type' => 'full' ) );?>
     98                        <p><a href="#" id="remove-groups-avatar"><?php esc_html_e( 'Remove Avatar', 'buddypress' ); ?></a></p>
     99                        <?php
     100                }
     101                ?>
     102        </div>
     103        <?php
     104        // Displays the button to Change the avatar.
     105        bp_attachments_browser( 'bp-avatar-upload', array(
     106                'item_id'         => $item->id,
     107                'component'       => 'groups',
     108                'item_type'       => 'avatar',
     109                'btn_caption'     => __( 'Edit Avatar', 'buddypress' ),
     110                'multi_selection' => false,
     111                'action'          => 'bp_avatar_upload',
     112                'file_data_name'  => 'file',
     113                'btn_class'       => 'attachments-new-avatar'
     114        ) );
     115    }
     116
     117    /**
     118         * Add a metabox to edit User's Avatar
     119         *
     120         * @access public
     121         * @since BuddyPress (2.2.0)
     122         */
     123    public function user_avatar( $user_id = 0, $screen_id = '' ) {
     124        // Set the screen ID if none was passed
     125                if ( empty( $screen_id ) ) {
     126                        $screen_id = buddypress()->members->admin->user_page;
     127                }
     128
     129                if ( bp_attachments_avatar_is_enabled() ) {
     130                        // Avatar Metabox
     131                        add_meta_box(
     132                                'bp_xprofile_avatar',
     133                                _x( 'Profile Photo', 'attachments user-admin edit screen', 'buddypress' ),
     134                                array( $this, 'user_avatar_metabox' ),
     135                                $screen_id,
     136                                'side',
     137                                'low'
     138                        );
     139                }
     140    }
     141
     142    /**
     143         * Displays the metabox to edit User's Avatar
     144         *
     145         * @access public
     146         * @since BuddyPress (2.2.0)
     147         */
     148    public function user_avatar_metabox( $user = null ) {
     149        if ( empty( $user->ID ) ) {
     150                        return;
     151                } ?>
     152
     153                <div id="xprofile-avatar">
     154
     155                        <?php echo bp_core_fetch_avatar( array(
     156                                'item_id' => $user->ID,
     157                                'object'  => 'user',
     158                                'type'    => 'full',
     159                                'title'   => $user->display_name
     160                        ) ); ?>
     161
     162                        <?php if ( bp_get_user_has_avatar( $user->ID ) ) : ?>
     163
     164                                <p><a href="#" id="remove-xprofile-avatar"><?php esc_html_e( 'Remove Avatar', 'buddypress' ); ?></a></p>
     165
     166                        <?php endif; ?>
     167
     168                </div>
     169                <?php
     170        // Displays the button to Change the avatar.
     171        bp_attachments_browser( 'bp-avatar-upload', array(
     172                'item_id'         => $user->ID,
     173                'component'       => 'xprofile',
     174                'item_type'       => 'avatar',
     175                'btn_caption'     => __( 'Edit Avatar', 'buddypress' ),
     176                'multi_selection' => false,
     177                'action'          => 'bp_avatar_upload',
     178                'file_data_name'  => 'file',
     179                'btn_class'       => 'attachments-new-avatar'
     180        ) );
     181    }
     182}
     183endif; // class_exists check
     184
     185// Load the BP Attachments admin
     186add_action( 'bp_init', array( 'BP_Attachments_Admin','register_attachments_admin' ) );
  • src/bp-attachments/bp-attachments-ajax.php

    diff --git src/bp-attachments/bp-attachments-ajax.php src/bp-attachments/bp-attachments-ajax.php
    index e69de29..f1021c3 100644
     
     1<?php
     2/**
     3 * Attachments Ajax.
     4 *
     5 * @package BuddyPress
     6 * @subpackage Attachments Ajax
     7 */
     8
     9// Exit if accessed directly
     10defined( 'ABSPATH' ) or exit;
     11
     12/**
     13 * Upload an avatar from "BP Attachments Editor".
     14 *
     15 * Avatars are not handled the same way than attachments
     16 * 1- no post type "bp_attachment" is created
     17 * 2- it doesn't change anything to the way BuddyPress manage avatars
     18 *
     19 * @since BuddyPress (2.2.0)
     20 */
     21function bp_attachments_avatar_ajax_upload(){
     22        //nonce check
     23        check_ajax_referer( 'media-form' );
     24
     25        $item_id = ! empty( $_REQUEST['item_id'] ) ? intval( $_REQUEST['item_id'] ) : null;
     26        $component = ! empty( $_REQUEST['component'] ) ? sanitize_title( $_REQUEST['component'] ) : '';
     27        $context = ! empty( $_REQUEST['item_type'] ) ? sanitize_title( $_REQUEST['item_type'] ) : '';
     28
     29        $cap_args = false;
     30        if ( ! empty( $component ) ) {
     31                $cap_args = array( 'component' => $component, 'item_id' => $item_id );
     32        }
     33
     34        // We don't categorized members as a bp_component term
     35        if ( 'members' == $component ) {
     36                $cap_args = false;
     37        }
     38
     39        // capability check
     40        if ( ! bp_attachments_current_user_can( 'edit_bp_attachments', $cap_args ) ) {
     41                wp_die();
     42        }
     43
     44        $avatar = bp_attachments_avatar_handle_upload( $_FILES, $component, $item_id, true );
     45
     46        if ( is_wp_error( $avatar ) ) {
     47                wp_send_json_error( array(
     48                        'message'  => $avatar->get_error_message(),
     49                        'filename' => $_FILES['file']['name'],
     50                ) );
     51        }
     52
     53        wp_send_json_success( $avatar );
     54}
     55add_action( 'wp_ajax_bp_avatar_upload', 'bp_attachments_avatar_ajax_upload' );
     56
     57/**
     58 * Crop an avatar from "BP Attachments Editor".
     59 *
     60 * Avatars are not handled the same way than attachments
     61 * 1- no post type "bp_attachment" is created
     62 * 2- it doesn't change anything to the way BuddyPress manage avatars
     63 *
     64 * @since BuddyPress (2.2.0)
     65 */
     66function bp_attachments_set_avatar() {
     67        $json = ! empty( $_REQUEST['json'] ); // New-style request
     68
     69        check_ajax_referer( 'bp_attachments_avatar', 'nonce' );
     70
     71        if ( empty( $_REQUEST['item_id'] ) || empty( $_REQUEST['object'] ) || empty( $_REQUEST['component'] ) ) {
     72                wp_die(0);
     73        }
     74
     75        $component = esc_html( $_REQUEST['component'] );
     76        $item_id   = absint( $_REQUEST['item_id'] );
     77        $object    = esc_html( $_REQUEST['object'] );
     78
     79        $cap_args = false;
     80        if ( ! empty( $component ) ) {
     81                $cap_args = array( 'component' => $component, 'item_id' => $item_id );
     82        }
     83
     84        // We don't categorized members as a bp_component term
     85        if ( 'members' == $component ) {
     86                $cap_args = false;
     87        }
     88
     89        // capability check
     90        if ( ! bp_attachments_current_user_can( 'edit_bp_attachments', $cap_args ) ) {
     91                wp_die();
     92        }
     93
     94        // Handle crop
     95        if ( bp_attachments_avatar_handle_crop( $_REQUEST ) ) {
     96                $return = bp_core_fetch_avatar( array(
     97                        'object'  => $object,
     98                        'item_id' => $item_id,
     99                        'html'    => true,
     100                        'type'    => 'full',
     101                ) );
     102
     103                $return .= '<p><a href="#" id="remove-' . $component . '-avatar">' . esc_html__( 'Remove Avatar', 'buddypress' ) . '</a></p>';
     104
     105                if ( ! empty( $json ) ) {
     106                        wp_send_json_success( $return );
     107                } else {
     108                        wp_die( $return );
     109                }
     110        } else {
     111                wp_die( 0 );
     112        }
     113}
     114add_action( 'wp_ajax_bp_attachments_set_avatar', 'bp_attachments_set_avatar' );
     115
     116/**
     117 * Delete an avatar from "BP Attachments Editor".
     118 *
     119 * Avatars are not handled the same way than attachments
     120 * 1- no post type "bp_attachment" is created
     121 * 2- it doesn't change anything to the way BuddyPress manage avatars
     122 *
     123 * @since BuddyPress (2.2.0)
     124 */
     125function bp_attachments_delete_avatar() {
     126        $json = ! empty( $_REQUEST['json'] ); // New-style request
     127
     128        check_ajax_referer( 'bp_attachments_avatar', 'nonce' );
     129
     130        if ( empty( $_REQUEST['item_id'] ) || empty( $_REQUEST['object'] ) ) {
     131                wp_die(0);
     132        }
     133
     134        $item_id = absint( $_REQUEST['item_id'] );
     135        $object = esc_html( $_REQUEST['object'] );
     136
     137        $cap_args = false;
     138        if ( ! empty( $object ) ) {
     139                $cap_args = array( 'component' => $object, 'item_id' => $item_id );
     140        }
     141
     142        // We don't categorized members as a bp_component term
     143        if ( 'members' == $object ) {
     144                $cap_args = false;
     145        }
     146
     147        // capability check
     148        if ( ! bp_attachments_current_user_can( 'edit_bp_attachments', $cap_args ) ) {
     149                wp_die();
     150        }
     151
     152        if ( bp_attachments_delete_existing_avatar( array( 'item_id' => $item_id, 'object' => $object ) ) ) {
     153                if ( ! empty( $json ) ) {
     154                        wp_send_json_success( 1 );
     155                } else {
     156                        wp_die( 1 );
     157                }
     158        } else {
     159                wp_die(0);
     160        }
     161}
     162add_action( 'wp_ajax_bp_attachments_delete_avatar', 'bp_attachments_delete_avatar' );
     163
     164/**
     165 * Query attachments for the "BP Attachments Editor".
     166 *
     167 * @since BuddyPress (2.2.0)
     168 */
     169function bp_attachments_query() {
     170        $query = isset( $_REQUEST['query'] ) ? (array) $_REQUEST['query'] : array();
     171        $query = array_intersect_key( $query, array_flip( array(
     172                's', 'order', 'orderby', 'posts_per_page', 'paged', 'post_mime_type',
     173                'post_parent', 'post__in', 'post__not_in', 'item_type', 'item_id', 'component'
     174        ) ) );
     175
     176        $args = array(
     177                'show_private'    => false,   // this could be checking bp_is_my_profile() or current user is a group admin
     178                'user_id'         => bp_loggedin_user_id(),
     179                'per_page'            => $query['posts_per_page'],
     180                'page'                => $query['paged'],
     181                'orderby'                 => $query['orderby'],
     182                'order'           => $query['order'],
     183        );
     184
     185        if ( ! empty( $query['item_id'] ) ) {
     186                $args['item_ids'] = array( $query['item_id'] );
     187        }
     188
     189        if ( ! empty( $query['component'] ) && 'members' != $query['component'] ) {
     190                $args['component'] = $query['component'];
     191        }
     192
     193        if ( ! empty( $query['s'] ) )
     194                $args['search'] = $query['s'];
     195
     196        $query = bp_attachments_get_attachments( $args );
     197
     198        $attachments = array_map( 'bp_attachments_prepare_attachment_for_js', $query['attachments'] );
     199        $attachments = array_filter( $attachments );
     200
     201        wp_send_json_success( $attachments );
     202}
     203add_action( 'wp_ajax_query_bp_attachments', 'bp_attachments_query' );
     204
     205/**
     206 * Process an attachment upload requested in "BP Attachments Editor".
     207 *
     208 * @since BuddyPress (2.2.0)
     209 */
     210function bp_attachments_upload() {
     211        //nonce check
     212        check_ajax_referer( 'media-form' );
     213
     214        $r = bp_parse_args( $_REQUEST, array(
     215                'item_id'         => 0,
     216                'component'       => '',
     217                'item_type'       => 'attachment',
     218                'action'          => 'bp_attachments_upload',
     219                'file_id'         => 'bp_attachment_file'
     220        ), 'attachments_ajax_upload' );
     221
     222        // We don't categorized members as a bp_component term
     223        if ( 'members' == $r['component'] ) {
     224                unset( $r['component'] );
     225        }
     226        $cap_args = false;
     227
     228        if ( ! empty( $r['component'] ) ) {
     229                $cap_args = array( 'component' => $r['component'], 'item_id' => $r['item_id'] );
     230        }
     231
     232        // capability check
     233        if ( ! bp_attachments_current_user_can( 'publish_bp_attachments', $cap_args ) ) {
     234                wp_die();
     235        }
     236
     237        $attachment_id = bp_attachments_handle_upload( $r );
     238
     239        if ( is_wp_error( $attachment_id ) ) {
     240                wp_send_json_error( array(
     241                        'message'  => $attachment_id->get_error_message(),
     242                        'filename' => $_FILES['bp_attachment_file']['name'],
     243                ) );
     244        }
     245
     246        if ( ! $attachment = bp_attachments_prepare_attachment_for_js( $attachment_id ) ) {
     247                wp_die();
     248        }
     249
     250        wp_send_json_success( $attachment );
     251}
     252add_action( 'wp_ajax_bp_attachments_upload', 'bp_attachments_upload' );
     253
     254/**
     255 * Delete an attachment from "BP Attachments Editor".
     256 *
     257 * @since BuddyPress (2.2.0)
     258 */
     259function bp_attachments_ajax_delete_attachment( $action ) {
     260        if ( empty( $action ) ) {
     261                $action = 'delete_bp_attachment';
     262        }
     263
     264        $id = isset( $_POST['id'] ) ? (int) $_POST['id'] : 0;
     265
     266        check_ajax_referer( "{$action}_$id" );
     267
     268        if ( ! bp_attachments_current_user_can( 'delete_bp_attachment', $id ) ) {
     269                wp_die( -1 );
     270        }
     271
     272        if ( bp_attachments_delete_attachment( $id ) ) {
     273                wp_die( 1 );
     274        } else {
     275                wp_die( 0 );
     276        }
     277}
     278add_action( 'wp_ajax_delete_bp_attachment', 'bp_attachments_ajax_delete_attachment' );
     279
     280/**
     281 * Update an attachment from "BP Attachments Editor".
     282 *
     283 * @since BuddyPress (2.2.0)
     284 */
     285function bp_attachments_ajax_update_attachment() {
     286        if ( ! isset( $_REQUEST['id'] ) || ! isset( $_REQUEST['changes'] ) ) {
     287                wp_send_json_error();
     288        }
     289
     290        if ( ! $id = absint( $_REQUEST['id'] ) ) {
     291                wp_send_json_error();
     292        }
     293
     294        check_ajax_referer( 'update_bp_attachment_' . $id, 'nonce' );
     295
     296        if ( ! bp_attachments_current_user_can( 'edit_bp_attachment', $id ) ) {
     297                wp_send_json_error();
     298        }
     299
     300        $changes = $_REQUEST['changes'];
     301
     302        $update = array( 'id' => $id, 'ajax' => true );
     303
     304        if ( isset( $changes['title'] ) ) {
     305                $update['title'] = $changes['title'];
     306        }
     307
     308        if ( isset( $changes['description'] ) ) {
     309                $update['description'] = $changes['description'];
     310        }
     311
     312        if ( ! bp_attachments_update_attachment( $update ) ) {
     313                wp_send_json_error();
     314        }
     315
     316        wp_send_json_success();
     317}
     318add_action( 'wp_ajax_update_bp_attachment', 'bp_attachments_ajax_update_attachment' );
  • src/bp-attachments/bp-attachments-caps.php

    diff --git src/bp-attachments/bp-attachments-caps.php src/bp-attachments/bp-attachments-caps.php
    index e69de29..973d9ce 100644
     
     1<?php
     2/**
     3 * Attachments caps
     4 *
     5 * @package BuddyPress
     6 * @subpackage Caps
     7 */
     8
     9// Exit if accessed directly
     10defined( 'ABSPATH' ) or exit;
     11
     12/**
     13 * bp_attachment post type caps
     14 *
     15 * @since BuddyPress (2.2.0)
     16 */
     17function bp_attachments_get_attachment_caps() {
     18        return apply_filters( 'bp_attachments_get_attachment_caps', array (
     19                'edit_posts'          => 'edit_bp_attachments',
     20                'edit_others_posts'   => 'edit_others_bp_attachments',
     21                'publish_posts'       => 'publish_bp_attachments',
     22                'read_private_posts'  => 'read_private_bp_attachments',
     23                'delete_posts'        => 'delete_bp_attachments',
     24                'delete_others_posts' => 'delete_others_bp_attachments'
     25        ) );
     26}
     27
     28/**
     29 * bp_component taxonomy caps
     30 *
     31 * @since BuddyPress (2.2.0)
     32 */
     33function bp_attachments_get_component_caps() {
     34        return apply_filters( 'bp_attachments_get_component_caps', array (
     35                'manage_terms' => 'manage_bp_components',
     36                'edit_terms'   => 'edit_bp_components',
     37                'delete_terms' => 'delete_bp_components',
     38                'assign_terms' => 'assign_bp_components'
     39        ) );
     40}
     41
     42/**
     43 * Map capabilities
     44 *
     45 * @since BuddyPress (2.2.0)
     46 *
     47 * @param  array   $caps
     48 * @param  string  $cap
     49 * @param  integer $user_id
     50 * @param  array   $args
     51 * @return array   $caps
     52 */
     53function bp_attachments_map_meta_caps( $caps = array(), $cap = '', $user_id = 0, $args = array() ) {
     54
     55        // What capability is being checked?
     56        switch ( $cap ) {
     57
     58                /** Reading ***********************************************************/
     59
     60                case 'read_private_bp_attachments' :
     61
     62                        if ( ! empty( $args[0] ) ) {
     63                                // Get the post
     64                                $_post = get_post( $args[0] );
     65                                if ( ! empty( $_post ) ) {
     66
     67                                        // Get caps for post type object
     68                                        $post_type = get_post_type_object( $_post->post_type );
     69                                        $caps      = array();
     70
     71                                        // Allow author to edit his attachment
     72                                        if ( $user_id == $_post->post_author ) {
     73                                                // In a network, most members has no role
     74                                                $caps[] = 'exist';
     75
     76                                        // @todo private attachments will need other rules
     77
     78                                        // Admins can always edit
     79                                        } else if ( user_can( $user_id, 'manage_options' ) ) {
     80                                                $caps = array( 'manage_options' );
     81                                        } else {
     82                                                $caps[] = $post_type->cap->edit_others_posts;
     83                                        }
     84
     85                                }
     86                        }
     87
     88
     89                        break;
     90
     91                /** Publishing ********************************************************/
     92
     93                case 'publish_bp_attachments' :
     94
     95                        if ( bp_is_my_profile() ) {
     96                                // In a network, most members has no role
     97                                $caps = array( 'exist' );
     98                        }
     99
     100                        if ( ! empty( $args[0] ) && is_a( $args[0], 'BP_Attachments_Can' ) ) {
     101
     102                                if ( ! empty( $args[0]->component ) && ! empty( $args[0]->item_id ) ){
     103                                        switch( $args[0]->component ) {
     104                                                case 'groups':
     105                                                        if( groups_is_user_member( $user_id, $args[0]->item_id ) ) {
     106                                                                // In a network, most members has no role
     107                                                                $caps = array( 'exist' );
     108                                                        }
     109                                                        break;
     110
     111                                                // and so on for other components
     112                                        }
     113                                }
     114
     115                        }
     116
     117                        // Admins can always publish
     118                        if ( user_can( $user_id, 'manage_options' ) ) {
     119                                $caps = array( 'manage_options' );
     120                        }
     121
     122                        break;
     123
     124                /** Editing ***********************************************************/
     125
     126                case 'edit_bp_attachments'        :
     127
     128                        if ( bp_is_my_profile() ) {
     129                                // In a network, most members has no role
     130                                $caps = array( 'exist' );
     131                        }
     132
     133                        if ( ! empty( $args[0] ) && is_a( $args[0], 'BP_Attachments_Can' ) ) {
     134
     135                                if ( ! empty( $args[0]->component ) && ! empty( $args[0]->item_id ) ){
     136                                        switch( $args[0]->component ) {
     137                                                case 'groups':
     138                                                case 'group' :
     139                                                        if( groups_is_user_admin( $user_id, $args[0]->item_id ) ) {
     140                                                                // In a network, most members has no role
     141                                                                $caps = array( 'exist' );
     142                                                        }
     143                                                        break;
     144
     145                                                // and so on for other components
     146                                        }
     147                                }
     148
     149                        }
     150
     151                        // Admins can always edit
     152                        if ( user_can( $user_id, 'manage_options' ) ) {
     153                                $caps = array( 'manage_options' );
     154                        }
     155
     156                        break;
     157
     158                // Used primarily in wp-admin
     159                case 'edit_others_bp_attachments' :
     160
     161                        // Admins can always edit
     162                        if ( user_can( $user_id, 'manage_options' ) ) {
     163                                $caps = array( 'manage_options' );
     164                        }
     165
     166                        break;
     167
     168                // Used everywhere
     169                case 'edit_bp_attachment' :
     170
     171                        // Get the post
     172                        $_post = get_post( $args[0] );
     173                        if ( ! empty( $_post ) ) {
     174
     175                                // Get caps for post type object
     176                                $post_type = get_post_type_object( $_post->post_type );
     177                                $caps      = array();
     178
     179                                // Allow author to edit his attachment
     180                                if ( $user_id == $_post->post_author ) {
     181                                        // In a network, most members has no role
     182                                        $caps[] = 'exist';
     183
     184                                // Admins can always edit
     185                                } else if ( user_can( $user_id, 'manage_options' ) ) {
     186                                        $caps = array( 'manage_options' );
     187                                } else {
     188                                        $caps[] = $post_type->cap->edit_others_posts;
     189                                }
     190
     191                        }
     192
     193                        break;
     194
     195                /** Deleting **********************************************************/
     196
     197                case 'delete_bp_attachment' :
     198
     199                        // Get the post
     200                        $_post = get_post( $args[0] );
     201                        if ( ! empty( $_post ) ) {
     202
     203                                // Get caps for post type object
     204                                $post_type = get_post_type_object( $_post->post_type );
     205                                $caps      = array();
     206
     207                                // Allow author to edit his attachment
     208                                if ( $user_id == $_post->post_author ) {
     209                                        // In a network, most members has no role
     210                                        $caps[] = 'exist';
     211
     212                                // Admins can always edit
     213                                } else if ( user_can( $user_id, 'manage_options' ) ) {
     214                                        $caps = array( 'manage_options' );
     215                                } else {
     216                                        $caps[] = $post_type->cap->delete_others_posts;
     217                                }
     218                        }
     219
     220                        break;
     221
     222                // Moderation override
     223                case 'delete_bp_attachments'        :
     224                case 'delete_others_bp_attachments' :
     225
     226                        // Moderators can always delete
     227                        if ( user_can( $user_id, 'manage_options' ) ) {
     228                                $caps = array( 'manage_options' );
     229                        }
     230
     231                        break;
     232
     233                /** Admin *************************************************************/
     234
     235                case 'bp_attachments_moderate' :
     236
     237                        // Admins can always moderate
     238                        if ( user_can( $user_id, 'manage_options' ) ) {
     239                                $caps = array( 'manage_options' );
     240                        }
     241
     242                        break;
     243
     244                /** bp component term ************************************************/
     245                case 'manage_bp_components'    :
     246                case 'edit_bp_components'      :
     247                case 'delete_bp_components'    :
     248                case 'bp_attachments_components_admin' :
     249
     250                        // Admins can always edit
     251                        if ( user_can( $user_id, 'manage_options' ) ) {
     252                                $caps = array( 'manage_options' );
     253                        }
     254
     255                        break;
     256
     257                // This should be improved..
     258                case 'assign_bp_components'   :
     259                        if ( is_user_logged_in() ) {
     260                                // In a network, most members has no role
     261                                $caps = array( 'exist' );
     262                        }
     263                        break;
     264
     265        }
     266
     267        return apply_filters( 'bp_attachments_map_meta_caps', $caps, $cap, $user_id, $args );
     268}
     269add_filter( 'bp_map_meta_caps', 'bp_attachments_map_meta_caps', 10, 4 );
     270
     271/**
     272 * Specific function to check a user's capability
     273 *
     274 * @since BuddyPress (2.2.0)
     275 *
     276 * @uses BP_Attachments_Can to create the argument to check upon
     277 * such as :
     278 * - component
     279 * - single item id
     280 * - attachment id
     281 */
     282function bp_attachments_current_user_can( $capability = '', $args = false ) {
     283        if ( ! empty( $args ) && is_array( $args ) )
     284                $args = new BP_Attachments_Can( $args );
     285
     286        $blog_id = bp_get_root_blog_id();
     287        $args = array( $blog_id, $capability, $args );
     288
     289        $retval = call_user_func_array( 'current_user_can_for_blog', $args );
     290
     291        return (bool) apply_filters( 'bp_attachments_current_user_can', $retval, $args );
     292}
  • src/bp-attachments/bp-attachments-classes.php

    diff --git src/bp-attachments/bp-attachments-classes.php src/bp-attachments/bp-attachments-classes.php
    index e69de29..588cab8 100644
     
     1<?php
     2/**
     3 * Attachments Classes.
     4 *
     5 * @package BuddyPress
     6 * @subpackage Attachments Classes
     7 */
     8
     9// Exit if accessed directly
     10defined( 'ABSPATH' ) or exit;
     11
     12/**
     13 * Attachments Upload Class.
     14 *
     15 * This class is used to handle upload
     16 * for components that are using  the
     17 * "bp_attachment"s post type
     18 *
     19 * @since BuddyPress (2.2.0)
     20 */
     21class BP_Attachments_Upload {
     22
     23        protected static $instance = null;
     24
     25        /**
     26         * Construct the uploader.
     27         *
     28         * @since BuddyPress (2.2.0)
     29         */
     30        public function __construct( $args = array() ) {
     31                $this->setup_globals( $args );
     32                $this->includes();
     33                $this->setup_actions();
     34                $this->upload();
     35                $this->reset_actions();
     36        }
     37
     38        /**
     39         * Starting point.
     40         *
     41         * @since BuddyPress (2.2.0)
     42         */
     43        public static function start( $args = array() ) {
     44                if ( empty( $args['action'] ) || empty( $args['file_id'] ) )
     45                        return;
     46
     47                // If the single instance hasn't been set, set it now.
     48                if ( null == self::$instance ) {
     49                        self::$instance = new self( $args );
     50                }
     51
     52                return self::$instance;
     53        }
     54
     55        /**
     56         * Define globals.
     57         *
     58         * @since BuddyPress (2.2.0)
     59         */
     60        public function setup_globals( $args = array() ) {
     61
     62                $this->attachment_id = 0;
     63
     64                $r = bp_parse_args( $args,  array(
     65                        'item_id'         => 0,                       // what is the item id ?
     66                        'component'       => '',                      // what is the component groups/members/messages ?
     67                        'item_type'       => 'attachment',            // attachment / avatar
     68                        'action'          => 'bp_attachments_upload', // are specific strings needed ?
     69                        'file_id'         => 'bp_attachment_file',    // the name of the $_FILE to upload
     70                ), 'attachments_uploader_args' );
     71
     72                foreach( $r as $key => $value ) {
     73                        $this->{$key} = $value;
     74                }
     75        }
     76
     77        /**
     78         * Include needed files.
     79         *
     80         * @since BuddyPress (2.2.0)
     81         */
     82        private function includes() {
     83                require_once( ABSPATH . '/wp-admin/includes/file.php' );
     84                require_once( ABSPATH . '/wp-admin/includes/media.php' );
     85                require_once( ABSPATH . '/wp-admin/includes/image.php' );
     86        }
     87
     88        /**
     89         * Actions and filters to run
     90         * before media_handle_upload() function is fired
     91         *
     92         * @since BuddyPress (2.2.0)
     93         */
     94        private function setup_actions() {
     95                // Filters
     96                add_filter( 'upload_dir',                array( $this, 'upload_dir' ),          10, 1 );
     97                add_filter( '_wp_relative_upload_path',  array( $this, 'relative_path' ),       10, 2 );
     98                add_filter( 'wp_insert_attachment_data', array( $this, 'attachment_data' ),     10, 2 );
     99                // Actions
     100                add_action( 'add_attachment',            array( $this, 'attachment_metadata' ), 10, 1 );
     101        }
     102
     103        /**
     104         * Upload!
     105         *
     106         * @since BuddyPress (2.2.0)
     107         */
     108        private function upload() {
     109                $attachment_data = array(
     110                        'post_type' => 'bp_attachment',
     111                        'context'   => 'buddypress',
     112                );
     113
     114                // components are terms, an attachment can be attached to more than one component
     115                if ( ! empty( $this->component ) ) {
     116                        $term = bp_attachments_get_component_term_id( $this->component );
     117
     118                        if ( ! empty( $term ) ) {
     119                                $attachment_data['tax_input'] = array( 'bp_component' => array( $term ) );
     120                        }
     121                }
     122
     123                $this->attachment_id = media_handle_upload( $this->file_id, 0, $attachment_data, array( 'action' => $this->action ) );
     124        }
     125
     126        /**
     127         * Actions and filters to remove
     128         * once media_handle_upload() function has done
     129         *
     130         * @since BuddyPress (2.2.0)
     131         */
     132        private function reset_actions() {
     133                // filters
     134                remove_filter( 'upload_dir',                array( $this, 'upload_dir' ),      10, 1 );
     135                remove_filter( '_wp_relative_upload_path',  array( $this, 'relative_path' ),   10, 2 );
     136                remove_filter( 'wp_insert_attachment_data', array( $this, 'attachment_data' ), 10, 2 );
     137                // actions
     138                remove_action( 'add_attachment',            array( $this, 'attachment_metadata' ), 10, 1 );
     139        }
     140
     141        /**
     142         * Filter upload dir
     143         *
     144         * @since BuddyPress (2.2.0)
     145         *
     146         * @todo handle private files using the post status
     147         * of the "bp_attachment" post type
     148         */
     149        public function upload_dir( $upload_data = array() ) {
     150                $bp = buddypress();
     151
     152                /**
     153                 * @todo private files!
     154                 *
     155                 * Using the post_status
     156                 */
     157
     158                $path = bp_attachments_get_upload_dir( 'attachments_public_dir' ) . '/' . bp_loggedin_user_id();
     159
     160                if ( ! file_exists( $path ) ) {
     161                        mkdir( $path );
     162                }
     163
     164                $url = bp_attachments_get_upload_dir( 'attachments_public_url' ) . '/' . bp_loggedin_user_id();
     165
     166                $r = bp_parse_args( array(
     167                        'path'    => $path,
     168                        'url'     => $url,
     169                        'subdir'  => false,
     170                        'basedir' => $path,
     171                        'baseurl' => $url,
     172                ), $upload_data, 'attachments_upload_dir_args' );
     173
     174                return $r;
     175        }
     176
     177        /**
     178         * Filter relative path
     179         *
     180         * @since BuddyPress (2.2.0)
     181         *
     182         * @todo handle private files using the post status
     183         * of the "bp_attachment" post type
     184         */
     185        public function relative_path( $new_path = '', $path = '' ) {
     186                $bp = buddypress();
     187
     188                /**
     189                 * @todo private files!
     190                 *
     191                 * Using the post_status
     192                 */
     193
     194                if ( ! bp_attachments_get_upload_dir( 'attachments_public_dir' ) ) {
     195                        return $new_path;
     196                }
     197
     198                if ( false !== strpos( $path, bp_attachments_get_upload_dir( 'attachments_public_dir' ) ) ) {
     199                        $new_path = str_replace( bp_attachments_get_upload_dir( 'attachments_dir' ), '', $path );
     200                        $new_path = basename( bp_attachments_get_upload_dir( 'attachments_dir' ) ) . $new_path;
     201                }
     202
     203                return $new_path;
     204        }
     205
     206        /**
     207         * Set the post type to bp_attachment
     208         * instead of attachmnent
     209         *
     210         * @since BuddyPress (2.2.0)
     211         */
     212        public function attachment_data( $data = array(), $object = array() ) {
     213                if( 'buddypress' == $object['context'] ) {
     214                        $post_name = sanitize_title( $object['post_title'] );
     215                        $post_name = wp_unique_post_slug( $post_name, 0, $data['post_status'], 'bp_attachment', $data['post_parent'] );
     216                        $data = array_merge( $data, array(
     217                                'post_type' => 'bp_attachment',
     218                                'post_name' => $post_name
     219                        ) );
     220                }
     221
     222                return $data;
     223        }
     224
     225        /**
     226         * Finally add post meta to link to a single item id of the component
     227         *
     228         * for instance a group id. It's possible to add an attachment to more
     229         * than one single item of the component
     230         *
     231         * @since BuddyPress (2.2.0)
     232         */
     233        public function attachment_metadata( $attachment_id = 0 ) {
     234                if ( empty( $attachment_id ) ) {
     235                        return;
     236                }
     237
     238                if ( ! empty( $this->component ) && ! empty( $this->item_id ) ) {
     239                        add_post_meta( $attachment_id, "_bp_{$this->component}_id", $this->item_id );
     240                }
     241        }
     242}
     243
     244/**
     245 * Attachments Browser Class.
     246 *
     247 * This class is used to create the
     248 * "BP Attachments Editor" for attachments
     249 * or Avatars
     250 *
     251 * @since BuddyPress (2.2.0)
     252 */
     253class BP_Attachments_Browser {
     254
     255        private static $settings = array();
     256
     257        private function __construct() {}
     258
     259        /**
     260         * Set the BP Attachments Editor settings
     261         *
     262         * @since BuddyPress (2.2.0)
     263         */
     264        public static function set( $browser_id, $settings ) {
     265                $set = bp_parse_args( $settings,  array(
     266                        'item_id'         => 0,                                     // what is the item id ?
     267                        'component'       => 'members',                             // what is the component groups/xprofile/members/messages ?
     268                        'item_type'       => 'avatar',                              // avatar / image custom..
     269                        'post_id'         => 0,
     270                        'btn_caption'     => __( 'Edit Avatar', 'buddypress' ), // the button caption
     271                        'btn_class'       => 'bp_avatar',                           // the button class
     272                        'file_data_name'  => 'bp_attachment_file',                  // the name of the $_FILE to upload
     273                        'multi_selection' => false,                                 // allow multiple file upload ?
     274                        'action'          => 'bp_attachments_upload',               // the ajax action to deal with the file
     275                        'callback'        => false,
     276                        'callback_id'     => false,
     277                ), 'attachments_browser_args' );
     278
     279                self::$settings = array_merge( $set, array( 'bp_attachments_button_id' => '#' . $browser_id ) );
     280                return $set;
     281        }
     282
     283        /**
     284         * Display the button to launch the BP Attachments Editor
     285         *
     286         * @since BuddyPress (2.2.0)
     287         */
     288        public static function browser( $browser_id, $settings = array() ) {
     289                $set = self::set( $browser_id, $settings );
     290
     291                $args = false;
     292                if ( ! empty( $set['component'] ) && ! empty( $set['item_id'] ) )
     293                        $args = wp_array_slice_assoc( $set, array( 'component', 'item_id' ) );
     294
     295                if ( bp_attachments_current_user_can( 'publish_bp_attachments', $args ) ) {
     296                        // Need some extra attribute to the wrapper
     297                        add_filter( 'bp_button_attachments_create-' . $set['component'] . '-' . $set['item_type'], array( __CLASS__, 'btn_extra_attribute' ), 10, 4 );
     298
     299                        bp_button( array(
     300                                'id'                => 'create-' . $set['component'] . '-' . $set['item_type'],
     301                                'component'         => 'attachments',
     302                                'must_be_logged_in' => true,
     303                                'block_self'        => false,
     304                                'wrapper_id'        => $browser_id,
     305                                'wrapper_class'     => $set['btn_class'],
     306                                'link_class'        => 'add-' .  $set['item_type'],
     307                                'link_href'         => '#',
     308                                'link_title'        => $set['btn_caption'],
     309                                'link_text'         => $set['btn_caption']
     310                        ) );
     311
     312                        remove_filter( 'bp_button_attachments_create-' . $set['component'] . '-' . $set['item_type'], array( __CLASS__, 'btn_extra_attribute' ), 10, 4 );
     313
     314                        // do not forget the fallback form if js is disabled or bp-media-editor out of service..
     315                        if ( ! is_admin() ) {
     316                                add_action( 'bp_attachments_uploader_fallback', array( __CLASS__, 'fallback_uploader' ), 10 );
     317                        }
     318                }
     319
     320                self::browser_settings( $browser_id );
     321        }
     322
     323        /**
     324         * This filter temporarly hide the button
     325         *
     326         * If no problem avoided the BP Attachments Editor to load
     327         * then the javascript media-editor.js will show the button
     328         * and remove the fallback uploader
     329         *
     330         * @since BuddyPress (2.2.0)
     331         */
     332        public static function btn_extra_attribute( $contents = '', $button = null, $before = '', $after ='' ) {
     333                $before = substr( $before, 0, -1 );
     334                $contents = str_replace( $before, $before .' style="display:none"', $contents );
     335
     336                return $contents;
     337        }
     338
     339        /**
     340         * Fallback uploader when no-js
     341         *
     342         * @since BuddyPress (2.2.0)
     343         */
     344        public static function fallback_uploader() {
     345                $settings = self::$settings;
     346                ?>
     347                <form action="" method="post" id="attachment-upload-form" class="standard-form" enctype="multipart/form-data">
     348                        <p id="attachment-upload">
     349                                <input type="file" name="<?php echo esc_attr( $settings['file_data_name'] );?>" id="file" />
     350                                <input type="submit" name="bp_attachment_upload" id="upload" value="<?php esc_attr_e( 'Upload', 'buddypress' ); ?>" />
     351
     352                                <?php if ( 'avatar' == $settings['item_type'] ) :?>
     353                                        <input type="hidden" name="item_type" id="item-type" value="<?php echo esc_attr( $settings['item_type'] );?>" />
     354                                <?php endif ;?>
     355
     356                                <?php if ( ! empty( $settings['component'] ) ) :?>
     357                                        <input type="hidden" name="component" id="component" value="<?php echo esc_attr( $settings['component'] );?>" />
     358                                <?php endif ;?>
     359
     360                                <?php if ( ! empty( $settings['item_id'] ) ) :?>
     361                                        <input type="hidden" name="item_id" id="item-id" value="<?php echo esc_attr( $settings['item_id'] );?>" />
     362                                <?php endif ;?>
     363
     364                                <?php if ( ! empty( $settings['post_id'] ) ) :?>
     365                                        <input type="hidden" name="post_id" id="post-id" value="<?php echo esc_attr( $settings['post_id'] );?>" />
     366                                <?php endif ;?>
     367                                <input type="hidden" name="action" id="action" value="<?php echo esc_attr( $settings['action'] );?>" />
     368                                <input type="hidden" name="file_data" id="file-data" value="<?php echo esc_attr( $settings['file_data_name'] );?>" />
     369                                <?php wp_nonce_field( 'bp_attachments_upload', 'bp_attachments_upload_nonce' ); ?>
     370                        </p>
     371                </form>
     372                <?php
     373        }
     374
     375        /**
     376         * Filter the Media Editor strings, settings & params
     377         * and enqueue the needed scripts
     378         *
     379         * @since BuddyPress (2.2.0)
     380         */
     381        public static function browser_settings( $browser_id ) {
     382                $settings = self::$settings;
     383
     384                $args = array();
     385                // Need to attach to a custom post types, let's use WordPress built in attachment features
     386                if ( ! empty( self::$settings['post_id'] ) ) {
     387                        $args = array( 'post' => absint( self::$settings['post_id'] ) );
     388                }
     389
     390                // media view filters
     391                add_filter( 'media_view_strings', array( __CLASS__, 'media_view_strings' ), 10, 1 );
     392                add_filter( 'media_view_settings', array( __CLASS__, 'media_view_settings' ), 10, 1 );
     393
     394                // Plupload filters
     395                add_filter( 'plupload_default_settings', array( __CLASS__, 'plupload_settings' ), 10, 1 );
     396                add_filter( 'plupload_default_params',   array( __CLASS__, 'plupload_params' ), 10, 1 );
     397
     398                // jcrop in case of avatar
     399                if ( 'avatar' == self::$settings['item_type'] ) {
     400                        wp_enqueue_style( 'jcrop' );
     401                        wp_enqueue_script( 'jcrop', array( 'jquery' ) );
     402                }
     403
     404                // Decide whether to load the dev version of the CSS and JavaScript
     405                $min = defined( 'SCRIPT_DEBUG' ) && SCRIPT_DEBUG ? '' : 'min.';
     406
     407                // time to enqueue scripts
     408                wp_enqueue_media( $args );
     409                wp_enqueue_script( 'bp-attachments-editor', buddypress()->plugin_url . "bp-attachments/js/script.{$min}js", array( 'media-editor' ), bp_get_version(), true );
     410        }
     411
     412        /**
     413         * Custom strings for the BP Attachments Editor
     414         *
     415         * @since BuddyPress (2.2.0)
     416         */
     417        public static function media_view_strings( $strings ) {
     418                $bp_attachments_strings = array( 'bp_attachments' => array() );
     419
     420                if ( 'avatar' == self::$settings['item_type'] ) {
     421                        $title = 'groups' == self::$settings['component'] ? __( 'Group Avatar', 'buddypress' ) : __( 'Avatar', 'buddypress' );
     422                        $bp_attachments_strings = array( 'bp_attachments' => array(
     423                                'title'     => $title,
     424                                'uploadtab' => __( 'Upload Avatar', 'buddypress' ),
     425                                'croptab'   => __( 'Crop Avatar', 'buddypress' ),
     426                        ) );
     427                }
     428
     429                if ( 'attachment' == self::$settings['item_type'] ) {
     430                        $title = 'groups' == self::$settings['component'] ? __( 'Group Attachments', 'buddypress' ) : __( 'Attachments', 'buddypress' );
     431                        $bp_attachments_strings = array( 'bp_attachments' => array(
     432                                'title'       => $title,
     433                                'uploadtab'   => __( 'Upload Attachments', 'buddypress' ),
     434                                'managetab'   => __( 'Manage Attachments', 'buddypress' ),
     435                        ) );
     436                }
     437
     438                if ( ! self::$settings['multi_selection'] ) {
     439                        $bp_attachments_strings['bp_attachments'] = array_merge(
     440                                $bp_attachments_strings['bp_attachments'],
     441                                array( 'files_error' => __( 'One file at a time', 'buddypress' ) )
     442                        );
     443                }
     444
     445                $bp_attachments_strings['bp_attachments'] = apply_filters( 'bp_attachments_strings', $bp_attachments_strings['bp_attachments'], self::$settings['bp_attachments_button_id'] );
     446                $bp_attachments_strings['bp_attachments'] = array_filter( $bp_attachments_strings['bp_attachments'] );
     447
     448                $strings = array_merge( $strings, $bp_attachments_strings );
     449
     450                return $strings;
     451        }
     452
     453        /**
     454         * Custom settings for the BP Attachments Editor
     455         *
     456         * @since BuddyPress (2.2.0)
     457         */
     458        public static function media_view_settings( $settings ) {
     459                $component = self::$settings['component'];
     460
     461                $settings['bp_attachments'] = array(
     462                        'item_id'   => self::$settings['item_id'],
     463                        'item_type' => $component,
     464                        'button_id' => self::$settings['bp_attachments_button_id'],
     465                );
     466
     467                if ( 'avatar' == self::$settings['item_type'] ) {
     468
     469                        switch ( $component ) {
     470                                case 'groups' :
     471                                        $object     = 'group';
     472                                        $avatar_dir = 'avatar_group_dir';
     473                                        break;
     474
     475                                case 'blogs' :
     476                                        $object     = 'blog';
     477                                        $avatar_dir = 'avatar_blog_dir';
     478                                        break;
     479
     480                                case 'xprofile' :
     481                                default         :
     482                                        $object     = 'user';
     483                                        $avatar_dir = 'avatar_user_dir';
     484                                        break;
     485                        }
     486
     487                        $settings['bp_attachments'] = array_merge(
     488                                $settings['bp_attachments'],
     489                                array(
     490                                        'full_h'     => bp_core_avatar_full_height(),
     491                                        'full_w'     => bp_core_avatar_full_width(),
     492                                        'object'     => $object,
     493                                        'avatar_dir' => $avatar_dir,
     494                                )
     495                        );
     496                }
     497
     498                return $settings;
     499        }
     500
     501        /**
     502         * Custom settings for plupload
     503         *
     504         * @since BuddyPress (2.2.0)
     505         */
     506        public static function plupload_settings( $settings ) {
     507                $settings['url'] = admin_url( 'admin-ajax.php' );
     508                $settings['file_data_name'] = self::$settings['file_data_name'];
     509                $settings['multi_selection'] = self::$settings['multi_selection'];
     510
     511                return $settings;
     512        }
     513
     514        /**
     515         * Custom params for plupload
     516         *
     517         * @since BuddyPress (2.2.0)
     518         */
     519        public static function plupload_params( $params ) {
     520                $params = array(
     521                        'action'      => self::$settings['action'],
     522                        'item_id'     => self::$settings['item_id'],
     523                        'component'   => self::$settings['component'],
     524                        'item_type'   => self::$settings['item_type'],
     525                        'post_id'     => self::$settings['post_id'],
     526                        '_bpnonce'    => wp_create_nonce( 'bp_attachments_' . self::$settings['item_type'] ),
     527                        'callback'    => self::$settings['callback'],
     528                        'callback_id' => self::$settings['callback_id'],
     529                );
     530
     531                return $params;
     532        }
     533}
     534
     535/**
     536 * Attachments "CRUD" Class.
     537 *
     538 * Create is handled by the Upload Class
     539 *
     540 * @since BuddyPress (2.2.0)
     541 */
     542class BP_Attachments {
     543        public $id;
     544        public $user_id;
     545        public $title;
     546        public $description;
     547        public $mime_type;
     548        public $guid;
     549        public $status;
     550        public $item_ids;
     551        public $components;
     552        public $attachment;
     553
     554        /**
     555         * Constructor.
     556         *
     557         * @since BuddyPress (2.2.0)
     558         */
     559        function __construct( $id = 0 ){
     560                if ( ! empty( $id ) ) {
     561                        $this->id = $id;
     562                        $this->populate();
     563                }
     564        }
     565
     566        /**
     567         * request an item id
     568         *
     569         * @uses get_post()
     570         * @uses wp_get_object_terms()
     571         */
     572        public function populate() {
     573                $this->attachment  = get_post( $this->id );
     574                $this->user_id     = $this->attachment->post_author;
     575                $this->title       = $this->attachment->post_title;
     576                $this->description = $this->attachment->post_content;
     577                $this->mime_type   = $this->attachment->post_mime_type;
     578                $this->guid        = $this->attachment->guid;
     579                $this->status      = $this->attachment->post_status;
     580                $this->components  = wp_get_object_terms( $this->id, 'bp_component', array( 'fields' => 'all' ) );
     581
     582                if ( ! empty( $this->components ) ) {
     583                        $this->item_ids = new stdClass();
     584                        foreach( (array) $this->components as $component ) {
     585                                /**
     586                                 * @todo transform ids into components slug
     587                                 * bp_attachments_get_component_term_id( $component_id )
     588                                 *
     589                                 * $component_id = bp_attachments_get_term_component_id( $component->term_id );
     590                                 * + Change $component->slug in favor of $component_id
     591                                 */
     592                                $this->item_ids->{$component->slug} = get_post_meta( $this->id, "_bp_{$component->slug}_id" );
     593                        }
     594                }
     595
     596        }
     597
     598        /**
     599         * Update an attachment.
     600         *
     601         * @since BuddyPress (2.2.0)
     602         * @todo changing from inherit (public) to private
     603         * This will need to move files from one dir to another
     604         */
     605        public function update() {
     606                $this->id          = apply_filters_ref_array( 'bp_attachments_id_before_update',          array( $this->id,          &$this ) );
     607                $this->user_id     = apply_filters_ref_array( 'bp_attachments_user_id_before_update',     array( $this->user_id,     &$this ) );
     608                $this->title       = apply_filters_ref_array( 'bp_attachments_title_before_update',       array( $this->title,       &$this ) );
     609                $this->description = apply_filters_ref_array( 'bp_attachments_description_before_update', array( $this->description, &$this ) );
     610                $this->status      = apply_filters_ref_array( 'bp_attachments_status_before_update',      array( $this->status,      &$this ) );
     611                $this->item_ids    = apply_filters_ref_array( 'bp_attachments_item_ids_before_update',    array( $this->item_ids,    &$this ) );
     612                $this->components  = apply_filters_ref_array( 'bp_attachments_components_before_update',  array( $this->components,  &$this ) );
     613
     614                // Use this, not the filters above
     615                do_action_ref_array( 'bp_attachments_before_update', array( &$this ) );
     616
     617                if ( ! $this->id || ! $this->user_id || ! $this->title )
     618                        return false;
     619
     620                if ( ! $this->status )
     621                        $this->status = 'inherit';
     622
     623                /**
     624                 * If the status changed, we'll need to move files
     625                 */
     626
     627                $update_args = array(
     628                        'ID'                 => $this->id,
     629                        'post_author'    => $this->user_id,
     630                        'post_title'     => $this->title,
     631                        'post_content'   => $this->description,
     632                        'post_type'              => 'bp_attachment',
     633                        'post_status'    => $this->status
     634                );
     635
     636                $updated = wp_update_post( $update_args );
     637
     638                $result = false;
     639
     640                if ( $updated ) {
     641                        $result = $this->title;
     642                }
     643
     644                do_action_ref_array( 'bp_attachments_after_update', array( &$this ) );
     645
     646                return $result;
     647        }
     648
     649        /**
     650         * The selection query
     651         *
     652         * @since BuddyPress (2.2.0)
     653         * @param array $args arguments to customize the query
     654         * @uses bp_parse_args
     655         */
     656        public static function get( $args = array() ) {
     657
     658                $defaults = array(
     659                        'item_ids'        => array(), // one or more item ids regarding the component (eg group_ids, message_ids )
     660                        'component'           => false,   // groups / messages / blogs / xprofile...
     661                        'show_private'    => false,   // wether to include private attachment
     662                        'user_id'             => false,   // the author id of the attachment
     663                        'post_id'         => false,
     664                        'per_page'            => 20,
     665                        'page'                => 1,
     666                        'search'          => false,
     667                        'exclude'                 => false,   // comma separated list or array of attachment ids.
     668                        'orderby'                 => 'modified',
     669                        'order'           => 'DESC',
     670                );
     671
     672                $r = bp_parse_args( $args, $defaults, 'attachments_query_args' );
     673
     674                $attachment_status = 'inherit';
     675
     676                if ( ! empty( $r['show_private'] ) ) {
     677                        $attachment_status = array( 'inherit', 'private' );
     678                }
     679
     680                $query_args = array(
     681                        'post_status'    => $attachment_status,
     682                        'post_type'          => 'bp_attachment',
     683                        'posts_per_page' => $r['per_page'],
     684                        'paged'              => $r['page'],
     685                        'orderby'                => $r['orderby'],
     686                        'order'          => $r['order'],
     687                );
     688
     689                if ( ! empty( $r['user_id'] ) ) {
     690                        $query_args['author'] = $r['user_id'];
     691                }
     692
     693                if ( ! empty( $r['exclude'] ) ) {
     694                        if ( ! is_array( $r['exclude'] ) ) {
     695                                $r['exclude'] = explode( ',', $r['exclude'] );
     696                        }
     697
     698                        $query_args['post__not_in'] = $r['exclude'];
     699                }
     700
     701                if ( ! empty( $r['post_id'] ) ) {
     702                        $query_args['post_parent'] = $r['post_id'];
     703                }
     704
     705                if ( ! empty( $r['component'] ) ) {
     706                        /**
     707                         * @todo transform slugs into ids using
     708                         * bp_attachments_get_component_term_id( $component_id )
     709                         *
     710                         * $component_ids = array_map( 'bp_attachments_get_component_term_id', $r['component'] )
     711                         * + Change the tax query in favor of
     712                         * array(
     713                         *              'taxonomy' => 'bp_component',
     714                         *              'field'    => 'term_id',
     715                         *              'terms'    => $component_ids
     716                         *      )
     717                         */
     718                        $query_args['tax_query'] = array(
     719                                array(
     720                                        'taxonomy' => 'bp_component',
     721                                        'field' => 'slug',
     722                                        'terms' => $r['component']
     723                                )
     724                        );
     725
     726                        $component = $r['component'];
     727
     728                        // component is defined, we can zoom on specific ids
     729                        if ( ! empty( $r['item_ids'] ) ) {
     730                                // We really want an array!
     731                                $item_ids = (array) $r['item_ids'];
     732
     733                                $query_args['meta_query'] = array(
     734                                        array(
     735                                                'key'     => "_bp_{$component}_id",
     736                                                'value'   => $item_ids,
     737                                                'compare' => 'IN',
     738                                        )
     739                                );
     740                        }
     741                }
     742
     743                $attachments = new WP_Query( $query_args );
     744
     745                return array( 'attachments' => $attachments->posts, 'total' => $attachments->found_posts );
     746        }
     747
     748        /**
     749         * Return the number of attachments depending on the context.
     750         *
     751         * Used in BuddyPress nav (group & user)
     752         *
     753         * @since BuddyPress (2.2.0)
     754         */
     755        public static function count( $args = array() ) {
     756                global $wpdb;
     757
     758                $r = bp_parse_args( $args, array(
     759                                'status'    => false,
     760                                'term_id'   => false,
     761                                'user_id'   => false,
     762                                'item_id'   => false,
     763                                'term_slug' => false,
     764                        )
     765                        , 'attachments_count_args'
     766                );
     767
     768                $sql = array();
     769                $detailed_count = array( 'total' => 0 );
     770
     771                $sql['select'] = "SELECT COUNT( p.ID ) as count, p.post_status as status, t.term_taxonomy_id as term_id";
     772                $sql['from'] = "FROM {$wpdb->posts} p LEFT JOIN {$wpdb->term_relationships} t ON( p.ID = t.object_id )";
     773                $sql['where'] = array( $wpdb->prepare( "p.post_type = %s", 'bp_attachment' ) );
     774                $sql['groupby'] = array( 'p.post_status', 't.term_taxonomy_id' );
     775
     776                if ( ! empty( $r['status'] ) ) {
     777                        $sql['where'][] = $wpdb->prepare( "p.post_status = %s", $r['status'] );
     778                }
     779
     780                if ( ! empty( $r['term_id'] ) ) {
     781                        $sql['where'][] = $wpdb->prepare( "t.term_taxonomy_id = %d", $r['term_id'] );
     782                }
     783
     784                if ( ! empty( $r['user_id'] ) ) {
     785                        $sql['where'][] = $wpdb->prepare( "p.post_author = %s", $r['user_id'] );
     786                }
     787
     788                if ( ! empty( $r['item_id'] ) && ! empty( $r['term_slug'] ) ) {
     789                        $sql['from'] .= " LEFT JOIN {$wpdb->postmeta} m ON ( p.ID = m.post_id )";
     790                        $sql['where'][] = $wpdb->prepare( "m.meta_key = %s AND m.meta_value = %d", '_bp_' . $r['term_slug'] .'_id', $r['item_id'] );
     791                }
     792
     793                // join
     794                $query = $sql['select'] . ' ' . $sql['from'] . ' WHERE ' . join( ' AND ', $sql['where'] ) . ' GROUP BY ' . join( ',', $sql['groupby'] );
     795
     796                $results = $wpdb->get_results( $query );
     797
     798                if ( empty( $results ) ) {
     799                        return $detailed_count;
     800                }
     801
     802                foreach( $results as $result ) {
     803                        if ( empty( $result->count ) )
     804                                continue;
     805
     806                        $count = absint( $result->count );
     807
     808                        $detailed_count['total'] += $count;
     809
     810                        if ( ! empty( $result->status ) ) {
     811                                // status
     812                                $detailed_count[ $result->status ] = empty( $detailed_count[ $result->status ] ) ? $count : absint( $detailed_count[ $result->status ] ) + $count;
     813
     814                                // terms
     815                                if ( ! empty( $result->term_id ) ) {
     816                                        $detailed_count[ $result->term_id ]['total'] = empty( $detailed_count[ $result->term_id ]['total'] ) ? $count : absint( $detailed_count[ $result->term_id ]['total'] ) + $count;
     817                                        $detailed_count[ $result->term_id ][ $result->status ] = empty( $detailed_count[ $result->term_id ][ $result->status ] ) ? $count : absint( $detailed_count[ $result->term_id ][ $result->status ] ) + $count;
     818                                }
     819                        }
     820                }
     821
     822                return $detailed_count;
     823        }
     824
     825        /**
     826         * Delete an attachment
     827         *
     828         * @since BuddyPress (2.2.0)
     829         * @see wp_delete_attachment() this is an adapted version..
     830         */
     831        public static function delete( $attachment_id = 0 ) {
     832                global $wpdb;
     833
     834                if( empty( $attachment_id ) ) {
     835                        return false;
     836                }
     837
     838                if ( ! $attachment = $wpdb->get_row( $wpdb->prepare("SELECT * FROM {$wpdb->posts} WHERE ID = %d", $attachment_id ) ) ) {
     839                        return $attachment;
     840                }
     841
     842                if ( 'bp_attachment' != $attachment->post_type ) {
     843                        return false;
     844                }
     845
     846                $meta = wp_get_attachment_metadata( $attachment_id );
     847                $file = get_attached_file( $attachment_id );
     848
     849                $intermediate_sizes = array();
     850                foreach ( get_intermediate_image_sizes() as $size ) {
     851                        if ( $intermediate = image_get_intermediate_size( $attachment_id, $size ) ) {
     852                                $intermediate_sizes[] = $intermediate;
     853                        }
     854                }
     855
     856                if ( is_multisite() ) {
     857                        delete_transient( 'dirsize_cache' );
     858                }
     859
     860                do_action( 'bp_attachments_before_attachment_delete', $attachment_id );
     861
     862                wp_delete_object_term_relationships( $attachment_id, get_object_taxonomies( $attachment->post_type ) );
     863
     864                delete_metadata( 'post', null, '_thumbnail_id', $attachment_id, true ); // delete all for any posts.
     865
     866                $post_meta_ids = $wpdb->get_col( $wpdb->prepare( "SELECT meta_id FROM {$wpdb->postmeta} WHERE post_id = %d", $attachment_id ) );
     867                foreach ( $post_meta_ids as $mid ) {
     868                        delete_metadata_by_mid( 'post', $mid );
     869                }
     870
     871                $result = $wpdb->delete( $wpdb->posts, array( 'ID' => $attachment_id ) );
     872                if ( ! $result ) {
     873                        return false;
     874                }
     875
     876                do_action( 'bp_attachments_after_attachment_db_delete', $attachment_id );
     877
     878                $uploadpath = wp_upload_dir();
     879
     880                if ( ! empty( $meta['thumb'] ) ) {
     881                        $thumbfile = str_replace( basename( $file), $meta['thumb'], $file );
     882                        @ unlink( path_join( $uploadpath['basedir'], $thumbfile ) );
     883                }
     884
     885                foreach ( $intermediate_sizes as $intermediate ) {
     886                        @ unlink( path_join( $uploadpath['basedir'], $intermediate['path'] ) );
     887                }
     888
     889                if ( ! empty( $file ) ) {
     890                        @ unlink( $file );
     891                }
     892
     893                clean_post_cache( $attachment );
     894
     895                do_action( 'bp_attachments_after_attachment_files_delete', $attachment_id );
     896
     897                return $attachment;
     898        }
     899}
     900
     901/**
     902 * Attachments Capability Class.
     903 *
     904 * This class is used to create the
     905 * arguments to check in the capability
     906 * mapping filter
     907 *
     908 * @since BuddyPress (2.2.0)
     909 */
     910class BP_Attachments_Can {
     911        public function __construct( $args = array() ) {
     912                if ( empty( $args ) ) {
     913                        return false;
     914                }
     915
     916                $r = bp_parse_args( $args, array(
     917                        'component'     => '',
     918                        'item_id'       => '',
     919                        'attachment_id' => ''
     920                ), 'attachments_can_args' );
     921
     922                foreach( $r as $key => $value ) {
     923                        $this->{$key} = $value;
     924                }
     925        }
     926}
  • src/bp-attachments/bp-attachments-cssjs.php

    diff --git src/bp-attachments/bp-attachments-cssjs.php src/bp-attachments/bp-attachments-cssjs.php
    index e69de29..d182bf5 100644
     
     1<?php
     2/**
     3 * Attachments CssJs.
     4 *
     5 * @package BuddyPress
     6 * @subpackage Attachments CssJs
     7 */
     8
     9// Exit if accessed directly
     10defined( 'ABSPATH' ) or exit;
     11
     12/**
     13 * Enqueues jCrop library and hooks BP's custom cropper JS.
     14 */
     15function bp_attachments_add_jquery_cropper() {
     16        wp_enqueue_style( 'jcrop' );
     17        wp_enqueue_script( 'jcrop', array( 'jquery' ) );
     18        add_action( 'wp_head', 'bp_attachments_add_cropper_inline_js' );
     19        add_action( 'wp_head', 'bp_attachments_add_cropper_inline_css' );
     20}
     21
     22/**
     23 * Output the inline JS needed for the cropper to work on a per-page basis.
     24 */
     25function bp_attachments_add_cropper_inline_js() {
     26
     27        // Bail if no image was uploaded
     28        $image = apply_filters( 'bp_inline_cropper_image', getimagesize( bp_attachments_get_upload_dir() . buddypress()->avatar_admin->image->dir ) );
     29        if ( empty( $image ) ) {
     30                return;
     31        }
     32
     33        // Get avatar full width and height
     34        $full_height = bp_core_avatar_full_height();
     35        $full_width  = bp_core_avatar_full_width();
     36
     37        // Calculate Aspect Ratio
     38        if ( !empty( $full_height ) && ( $full_width != $full_height ) ) {
     39                $aspect_ratio = $full_width / $full_height;
     40        } else {
     41                $aspect_ratio = 1;
     42        }
     43
     44        // Default cropper coordinates
     45
     46        // Smaller than full-width: cropper defaults to entire image
     47        if ( $image[0] < $full_width ) {
     48                $crop_left  = 0;
     49                $crop_right = $image[0];
     50
     51        // Less than 2x full-width: cropper defaults to full-width
     52        } else if ( $image[0] < ( $full_width * 2 ) ) {
     53                $padding_w  = round( ( $image[0] - $full_width ) / 2 );
     54                $crop_left  = $padding_w;
     55                $crop_right = $image[0] - $padding_w;
     56
     57        // Larger than 2x full-width: cropper defaults to 1/2 image width
     58        } else {
     59                $crop_left  = round( $image[0] / 4 );
     60                $crop_right = $image[0] - $crop_left;
     61        }
     62
     63        // Smaller than full-height: cropper defaults to entire image
     64        if ( $image[1] < $full_height ) {
     65                $crop_top    = 0;
     66                $crop_bottom = $image[1];
     67
     68        // Less than double full-height: cropper defaults to full-height
     69        } else if ( $image[1] < ( $full_height * 2 ) ) {
     70                $padding_h   = round( ( $image[1] - $full_height ) / 2 );
     71                $crop_top    = $padding_h;
     72                $crop_bottom = $image[1] - $padding_h;
     73
     74        // Larger than 2x full-height: cropper defaults to 1/2 image height
     75        } else {
     76                $crop_top    = round( $image[1] / 4 );
     77                $crop_bottom = $image[1] - $crop_top;
     78        }
     79
     80        ?>
     81
     82        <script type="text/javascript">
     83                jQuery(window).load( function(){
     84                        jQuery('#avatar-to-crop').Jcrop({
     85                                onChange: showPreview,
     86                                onSelect: updateCoords,
     87                                aspectRatio: <?php echo (int) $aspect_ratio; ?>,
     88                                setSelect: [ <?php echo (int) $crop_left; ?>, <?php echo (int) $crop_top; ?>, <?php echo (int) $crop_right; ?>, <?php echo (int) $crop_bottom; ?> ]
     89                        });
     90                        updateCoords({x: <?php echo (int) $crop_left; ?>, y: <?php echo (int) $crop_top; ?>, w: <?php echo (int) $crop_right; ?>, h: <?php echo (int) $crop_bottom; ?>});
     91                });
     92
     93                function updateCoords(c) {
     94                        jQuery('#x').val(c.x);
     95                        jQuery('#y').val(c.y);
     96                        jQuery('#w').val(c.w);
     97                        jQuery('#h').val(c.h);
     98                }
     99
     100                function showPreview(coords) {
     101                        if ( parseInt(coords.w) > 0 ) {
     102                                var fw = <?php echo (int) $full_width; ?>;
     103                                var fh = <?php echo (int) $full_height; ?>;
     104                                var rx = fw / coords.w;
     105                                var ry = fh / coords.h;
     106
     107                                jQuery( '#avatar-crop-preview' ).css({
     108                                        width: Math.round(rx * <?php echo (int) $image[0]; ?>) + 'px',
     109                                        height: Math.round(ry * <?php echo (int) $image[1]; ?>) + 'px',
     110                                        marginLeft: '-' + Math.round(rx * coords.x) + 'px',
     111                                        marginTop: '-' + Math.round(ry * coords.y) + 'px'
     112                                });
     113                        }
     114                }
     115        </script>
     116
     117<?php
     118}
     119
     120/**
     121 * Output the inline CSS for the BP image cropper.
     122 *
     123 * @package BuddyPress Core
     124 */
     125function bp_attachments_add_cropper_inline_css() {
     126?>
     127
     128        <style type="text/css">
     129                .jcrop-holder { float: left; margin: 0 20px 20px 0; text-align: left; }
     130                #avatar-crop-pane { width: <?php echo bp_core_avatar_full_width() ?>px; height: <?php echo bp_core_avatar_full_height() ?>px; overflow: hidden; }
     131                #avatar-crop-submit { margin: 20px 0; }
     132                .jcrop-holder img,
     133                #avatar-crop-pane img,
     134                #avatar-upload-form img,
     135                #create-group-form img,
     136                #group-settings-form img { border: none !important; max-width: none !important; }
     137        </style>
     138
     139<?php
     140}
  • src/bp-attachments/bp-attachments-functions.php

    diff --git src/bp-attachments/bp-attachments-functions.php src/bp-attachments/bp-attachments-functions.php
    index e69de29..0879b54 100644
     
     1<?php
     2/**
     3 * Attachments functions
     4 *
     5 * @package BuddyPress
     6 * @subpackage Attachments Functions
     7 */
     8
     9// Exit if accessed directly
     10defined( 'ABSPATH' ) or exit;
     11
     12/**
     13 * Check avatar uploads settings
     14 *
     15 * @since BuddyPress (2.2.0)
     16 */
     17function bp_attachments_avatar_is_enabled() {
     18        $bp = buddypress();
     19
     20        if ( ! (int) $bp->site_options['bp-disable-avatar-uploads'] && $bp->avatar->show_avatars ) {
     21                return true;
     22        }
     23        return false;
     24}
     25
     26/**
     27 * Create the upload directories
     28 *
     29 * @since BuddyPress (2.2.0)
     30 *
     31 * @uses    is_multisite()
     32 * @uses    switch_to_blog()
     33 * @uses    bp_get_root_blog_id()
     34 * @uses    wp_upload_dir()
     35 * @uses    restore_current_blog()
     36 * @uses    wp_mkdir_p()
     37 * @uses    apply_filters() call 'bp_attachments_use_attachments_api' to create the dirs
     38 *                          required by the BP Attachments API
     39 * @return  array list of upload urls and dirs
     40 */
     41function bp_attachments_set_upload_dirs() {
     42        // We need to switch to the root blog on multisite installs
     43        if ( is_multisite() ) {
     44                switch_to_blog( bp_get_root_blog_id() );
     45        }
     46
     47        // Get upload directory information from current site
     48        $upload_datas = wp_upload_dir();
     49
     50        // Will bail if not switched
     51        restore_current_blog();
     52
     53        $bp_attachments_sub_dirs = array(
     54                'avatar_user'  => array( 'main' => 'avatars' ),
     55        );
     56
     57        if ( bp_is_active( 'groups' ) ) {
     58                $bp_attachments_sub_dirs['avatar_group'] = array( 'main' => 'group-avatars' );
     59        }
     60
     61        if ( bp_is_active( 'blogs' ) ) {
     62                $bp_attachments_sub_dirs['avatar_blog'] = array( 'main' => 'blog-avatars' );
     63        }
     64
     65        if ( buddypress()->attachments->use_api ) {
     66                $bp_attachments_sub_dirs = array_merge( $bp_attachments_sub_dirs, array(
     67                        'attachments' => array( 'main' => 'bp-attachments', 'subs' => array( 'public', 'private' ) ),
     68                ) );
     69        }
     70
     71        $bp_attachments_upload_data = array();
     72        $avatar_upload_data         = array();
     73
     74        if ( is_ssl() ) {
     75                $upload_datas["baseurl"] = str_replace( 'http://', 'https://', $upload_datas["baseurl"] );
     76        }
     77
     78        foreach ( $bp_attachments_sub_dirs as $key => $dirs ) {
     79                if ( false !== strpos( $key, 'avatar' ) ) {
     80                        $avatar_upload_data = array_merge( $avatar_upload_data, array(
     81                                $key . '_dir' => $upload_datas["basedir"] .'/' . $dirs['main'],
     82                                $key . '_url' => $upload_datas["baseurl"] .'/' . $dirs['main'],
     83                        ) );
     84
     85                        if ( defined( 'BP_AVATAR_UPLOAD_PATH' ) ) {
     86                                $avatar_upload_data[ $key . '_dir' ] = trailingslashit( BP_AVATAR_UPLOAD_PATH ) . $dirs['main'];
     87                        }
     88
     89                        if ( defined( 'BP_AVATAR_URL' ) ) {
     90                                $avatar_upload_data[ $key . '_url' ] = trailingslashit( BP_AVATAR_URL ) . $dirs['main'];
     91                        }
     92
     93                } else if ( 'attachments' == $key ) {
     94                        // basedir and baseurl for BuddyPress attachments
     95                        $bp_attachments_upload_data = array(
     96                                $key . '_dir' => $upload_datas["basedir"] . '/' . $dirs['main'],
     97                                $key . '_url' => $upload_datas["baseurl"] . '/' . $dirs['main'],
     98                        );
     99
     100                        // Loop in subs
     101                        foreach ( $dirs['subs'] as $sub ) {
     102                                $bp_attachments_upload_data = array_merge( $bp_attachments_upload_data, array(
     103                                        $key . '_' . $sub . '_dir' => $upload_datas["basedir"] . '/' . $dirs['main'] . '/' . $sub,
     104                                        $key . '_' . $sub . '_url' => $upload_datas["baseurl"] . '/' . $dirs['main'] . '/' . $sub,
     105                                ) );
     106                        }
     107                }
     108        }
     109
     110        // Append Avatar upload data
     111        $bp_attachments_upload_data = array_merge( $avatar_upload_data, $bp_attachments_upload_data );
     112
     113        // Loop in dirs to eventually create them if they do not exist
     114        foreach ( $bp_attachments_upload_data as $key_upload_dir => $upload_dir ) {
     115
     116                if( ! file_exists( $upload_dir ) ) {
     117                        @wp_mkdir_p( $upload_dir );
     118
     119                        // Do additional actions (for instance create an .htaccess file for private dir)
     120                        do_action( 'bp_attachments_create_upload_dir', $key_upload_dir, $upload_dir );
     121                }
     122        }
     123
     124        // Do backcompat for bp_core_get_upload_dir()
     125        $bp_attachments_upload_data = array_merge( array(
     126                'upload_path' => $upload_datas["basedir"],
     127                'url'         => $upload_datas["baseurl"],
     128        ), $bp_attachments_upload_data );
     129
     130        // finally returns upload_data
     131        return $bp_attachments_upload_data;
     132}
     133
     134/**
     135 * Get the upload dir/url
     136 *
     137 * @since BuddyPress (2.2.0)
     138 *
     139 * @param  string $type    type dir/url to get
     140 * @uses   apply_filters() call 'bp_attachments_use_attachments_api' to get the dirs
     141 *                         of the BP Attachments API
     142 * @return string          upload url or path
     143 */
     144function bp_attachments_get_upload_dir( $type = 'upload_path' ) {
     145        $possible_matches = array(
     146                'upload_path'     => 1,
     147                'url'             => 1,
     148                'avatar_user_dir' => 1,
     149                'avatar_user_url' => 1,
     150        );
     151
     152        if ( bp_is_active( 'groups' ) ) {
     153                $possible_matches = array_merge( $possible_matches, array(
     154                        'avatar_group_dir' => 1,
     155                        'avatar_group_url' => 1,
     156                ) );
     157        }
     158
     159        if ( bp_is_active( 'blogs' ) ) {
     160                $possible_matches = array_merge( $possible_matches, array(
     161                        'avatar_blog_dir' => 1,
     162                        'avatar_blog_url' => 1,
     163                ) );
     164        }
     165
     166        if ( buddypress()->attachments->use_api ) {
     167                $possible_matches = array_merge( $possible_matches, array(
     168                        'attachments_dir'         => 1,
     169                        'attachments_url'         => 1,
     170                        'attachments_public_dir'  => 1,
     171                        'attachments_public_url'  => 1,
     172                        'attachments_private_dir' => 1,
     173                        'attachments_private_url' => 1,
     174                ) );
     175        }
     176
     177        // Only return upload datas
     178        if ( empty( $possible_matches[ $type ] ) ) {
     179                return false;
     180        }
     181
     182        $hook_prefix = 'bp_attachments_get_';
     183
     184        // Backcompat
     185        if ( 'upload_path' == $type || 'url' == $type ) {
     186                $hook_prefix = 'bp_core_avatar_';
     187        }
     188
     189        return apply_filters( $hook_prefix . $type , buddypress()->attachments->{$type} );
     190}
     191
     192/**
     193 * Search for local avatar data and return an avatar object if any
     194 *
     195 * @since BuddyPress (2.2.0)
     196 *
     197 * @param  object $avatar
     198 * @return object the avatar object with local data if found
     199 */
     200function bp_attachments_avatar_local_data( $avatar = null ) {
     201        if ( empty( $avatar->avatar_dir ) || ! bp_attachments_avatar_is_enabled() ) {
     202                return $avatar;
     203        }
     204
     205        // Set img URL and DIR based on prepopulated constants
     206        $avatar->folder_dir = bp_attachments_get_upload_dir( $avatar->avatar_dir . '_dir' );
     207
     208        if ( empty( $avatar->folder_dir ) ) {
     209                $avatar->loc_path  = trailingslashit( bp_attachments_get_upload_dir() );
     210                $avatar->loc_url   = trailingslashit( bp_attachments_get_upload_dir( 'url' ) );
     211
     212                $avatar->loc_dir    = trailingslashit( $avatar->avatar_dir );
     213                $avatar->folder_url = apply_filters( 'bp_core_avatar_folder_url', ( $avatar->loc_url  . $avatar->loc_dir . $avatar->item_id ), $avatar->item_id, $avatar->object, $avatar->avatar_dir );
     214                $avatar->folder_dir = apply_filters( 'bp_core_avatar_folder_dir', ( $avatar->loc_path . $avatar->loc_dir . $avatar->item_id ), $avatar->item_id, $avatar->object, $avatar->avatar_dir );
     215        } else {
     216                $avatar->folder_dir = apply_filters( 'bp_core_avatar_folder_dir', trailingslashit( $avatar->folder_dir ) . $avatar->item_id, $avatar->item_id, $avatar->object, $avatar->avatar_dir );
     217                $avatar->folder_url = bp_attachments_get_upload_dir( $avatar->avatar_dir . '_url' );
     218                $avatar->folder_url = apply_filters( 'bp_core_avatar_folder_url', trailingslashit( $avatar->folder_url ) . $avatar->item_id, $avatar->item_id, $avatar->object, $avatar->avatar_dir );
     219        }
     220
     221        /**
     222         * Look for uploaded avatar first. Use it if it exists.
     223         * Set the file names to search for, to select the full size
     224         * or thumbnail image.
     225         */
     226        $avatar_size              = ( 'full' == $avatar->type ) ? '-bpfull' : '-bpthumb';
     227        $legacy_user_avatar_name  = ( 'full' == $avatar->type ) ? '-avatar2' : '-avatar1';
     228        $legacy_group_avatar_name = ( 'full' == $avatar->type ) ? '-groupavatar-full' : '-groupavatar-thumb';
     229
     230        // Check for directory
     231        if ( file_exists( $avatar->folder_dir ) ) {
     232
     233                // Open directory
     234                if ( $av_dir = opendir( $avatar->folder_dir ) ) {
     235
     236                        // Stash files in an array once to check for one that matches
     237                        $avatar_files = array();
     238                        while ( false !== ( $avatar_file = readdir( $av_dir ) ) ) {
     239                                // Only add files to the array (skip directories)
     240                                if ( 2 < strlen( $avatar_file ) ) {
     241                                        $avatar_files[] = $avatar_file;
     242                                }
     243                        }
     244
     245                        // Check for array
     246                        if ( 0 < count( $avatar_files ) ) {
     247
     248                                // Check for current avatar
     249                                foreach( $avatar_files as $key => $value ) {
     250                                        if ( strpos ( $value, $avatar_size )!== false ) {
     251                                                $avatar->url = $avatar->folder_url . '/' . $avatar_files[$key];
     252                                        }
     253                                }
     254
     255                                // Legacy avatar check
     256                                if ( empty( $avatar->url ) ) {
     257                                        foreach( $avatar_files as $key => $value ) {
     258                                                if ( strpos ( $value, $legacy_user_avatar_name )!== false ) {
     259                                                        $avatar->url = $avatar->folder_url . '/' . $avatar_files[$key];
     260                                                }
     261                                        }
     262
     263                                        // Legacy group avatar check
     264                                        if ( empty( $avatar->url ) ) {
     265                                                foreach( $avatar_files as $key => $value ) {
     266                                                        if ( strpos ( $value, $legacy_group_avatar_name )!== false ) {
     267                                                                $avatar->url = $avatar->folder_url . '/' . $avatar_files[$key];
     268                                                        }
     269                                                }
     270                                        }
     271                                }
     272                        }
     273                }
     274        }
     275
     276        return $avatar;
     277}
     278add_filter( 'bp_core_avatar_local_data', 'bp_attachments_avatar_local_data', 10, 1 );
     279
     280/**
     281 * Delete an existing avatar.
     282 *
     283 * @since BuddyPress (2.2.0)
     284 *
     285 * @param array $args {
     286 *     Array of function parameters.
     287 *     @type bool|int $item_id ID of the item whose avatar you're deleting.
     288 *           Defaults to the current item of type $object.
     289 *     @type string $object Object type of the item whose avatar you're
     290 *           deleting. 'user', 'group', 'blog', or custom. Default: 'user'.
     291 *     @type bool|string $avatar_dir Subdirectory where avatar is located.
     292 *           Default: false, which falls back on the default location
     293 *           corresponding to the $object.
     294 * }
     295 * @return bool True on success, false on failure.
     296 */
     297function bp_attachments_delete_existing_avatar( $args = '' ) {
     298
     299        $defaults = array(
     300                'item_id'    => false,
     301                'object'     => 'user', // user OR group OR blog OR custom type (if you use filters)
     302                'avatar_dir' => false
     303        );
     304
     305        $r = wp_parse_args( $args, $defaults );
     306
     307        if ( empty( $r['item_id'] ) ) {
     308                if ( 'user' == $r['object'] ) {
     309                        $r['item_id'] = bp_displayed_user_id();
     310                } else if ( 'group' == $r['object'] ) {
     311                        $r['item_id'] = buddypress()->groups->current_group->id;
     312                } else if ( 'blog' == $r['object'] ) {
     313                        $r['item_id'] = get_current_blog_id();
     314                }
     315
     316                $r['item_id'] = apply_filters( 'bp_core_avatar_item_id', $r['item_id'], $r['object'] );
     317
     318                if ( ! $r['item_id'] ) {
     319                        return false;
     320                }
     321        }
     322
     323        if ( empty( $r['avatar_dir'] ) ) {
     324                if ( 'user' == $r['object'] ) {
     325                        $r['avatar_dir'] = 'avatar_user_dir';
     326                } else if ( 'group' == $r['object'] ) {
     327                        $r['avatar_dir'] = 'avatar_group_dir';
     328                } else if ( 'blog' == $r['object'] ) {
     329                        $r['avatar_dir'] = 'avatar_blog_dir';
     330                }
     331
     332                $r['avatar_dir'] = apply_filters( 'bp_core_avatar_dir', $r['avatar_dir'], $r['object'] );
     333
     334                if ( ! $r['avatar_dir'] ) {
     335                        return false;
     336                }
     337        }
     338
     339        $avatar_folder_dir = bp_attachments_get_upload_dir( $r['avatar_dir'] );
     340
     341        // Backcompat
     342        if ( empty( $avatar_folder_dir ) ) {
     343                $avatar_folder_dir = bp_attachments_get_upload_dir() . '/' . $r['avatar_dir'];
     344        }
     345
     346        $avatar_folder_dir .= '/' . $r['item_id'];
     347
     348        $avatar_folder_dir = apply_filters( 'bp_core_avatar_folder_dir', $avatar_folder_dir, $r['item_id'], $r['object'], $r['avatar_dir'] );
     349
     350        if ( ! file_exists( $avatar_folder_dir ) ) {
     351                return false;
     352        }
     353
     354        if ( $av_dir = opendir( $avatar_folder_dir ) ) {
     355                while ( false !== ( $avatar_file = readdir($av_dir) ) ) {
     356                        if ( ( preg_match( "/-bpfull/", $avatar_file ) || preg_match( "/-bpthumb/", $avatar_file ) ) && '.' != $avatar_file && '..' != $avatar_file ) {
     357                                @unlink( $avatar_folder_dir . '/' . $avatar_file );
     358                        }
     359                }
     360        }
     361        closedir($av_dir);
     362
     363        @rmdir( $avatar_folder_dir );
     364
     365        do_action( 'bp_core_delete_existing_avatar', $args );
     366
     367        return true;
     368}
     369
     370/**
     371 * Setup the avatar upload directory for a user.
     372 *
     373 * @since BuddyPress (2.2.0)
     374 *
     375 * @package BuddyPress Core
     376 *
     377 * @param string $directory The root directory name. Optional.
     378 * @param int    $user_id   The user ID. Optional.
     379 *
     380 * @return array() Array containing the path, URL, and other helpful settings.
     381 */
     382function bp_attachments_xprofile_avatar_upload_dir( $directory = 'avatar_user_dir', $user_id = 0 ) {
     383        $bp = buddypress();
     384
     385        // Use displayed user if no user ID was passed
     386        if ( empty( $user_id ) ) {
     387                // Default
     388                $user_id = bp_displayed_user_id();
     389
     390                // In Profile administration screen displayed user id is not set
     391                if ( ! empty( $bp->avatar_admin->image->item_id ) && 'xprofile' == $bp->avatar_admin->image->component ) {
     392                        $user_id = $bp->avatar_admin->image->item_id;
     393                }
     394        }
     395
     396        // Failsafe against accidentally nooped $directory parameter
     397        if ( empty( $directory ) ) {
     398                $directory = 'avatar_user_dir';
     399        }
     400
     401        $avatar_folder_dir = bp_attachments_get_upload_dir( $directory );
     402
     403        // Backcompat
     404        if ( empty( $avatar_folder_dir ) ) {
     405                $avatar_folder_dir = bp_attachments_get_upload_dir() . '/' . $directory;
     406        }
     407
     408        $path    = $avatar_folder_dir . '/' . $user_id;
     409        $newbdir = $path;
     410
     411        if ( ! file_exists( $path ) ) {
     412                @wp_mkdir_p( $path );
     413        }
     414
     415        // Backcompat
     416        if ( 'avatar_user_dir' != $directory ) {
     417                $newurl = bp_attachments_get_upload_dir( 'url' ) . '/' . $directory. '/' . $user_id;
     418        } else {
     419                $newurl = bp_attachments_get_upload_dir( 'avatar_user_url' ) . '/' . $user_id;
     420        }
     421
     422        $newburl   = $newurl;
     423
     424        return apply_filters( 'xprofile_avatar_upload_dir', array(
     425                'path'    => $path,
     426                'url'     => $newurl,
     427                'subdir'  => false,
     428                'basedir' => $newbdir,
     429                'baseurl' => $newburl,
     430                'error'   => false
     431        ) );
     432}
     433
     434/**
     435 * Generate the avatar upload directory path for a given group.
     436 *
     437 * @since BuddyPress (2.2.0)
     438 *
     439 * @param int $group_id Optional. ID of the group. Default: ID of the
     440 *        current group.
     441 * @return string
     442 */
     443function bp_attachments_groups_avatar_upload_dir( $group_id = 0 ) {
     444        $bp = buddypress();
     445
     446        // Bail if groups component is not active
     447        if ( ! bp_is_active( 'groups' ) ) {
     448                return false;
     449        }
     450
     451        if ( empty( $group_id ) ) {
     452                $group_id = bp_get_current_group_id();
     453
     454                // In Group administration screen current group id is not set
     455                if ( ! empty( $bp->avatar_admin->image->item_id ) && 'groups' == $bp->avatar_admin->image->component ) {
     456                        $group_id = $bp->avatar_admin->image->item_id;
     457                }
     458        }
     459
     460        $path    = bp_attachments_get_upload_dir( 'avatar_group_dir' ) . '/' . $group_id;
     461        $newbdir = $path;
     462
     463        if ( ! file_exists( $path ) ) {
     464                @wp_mkdir_p( $path );
     465        }
     466
     467        $newurl    = bp_attachments_get_upload_dir( 'avatar_group_url' ) . '/' . $group_id;
     468        $newburl   = $newurl;
     469
     470        return apply_filters( 'groups_avatar_upload_dir', array(
     471                'path'    => $path,
     472                'url'     => $newurl,
     473                'subdir'  => false,
     474                'basedir' => $newbdir,
     475                'baseurl' => $newburl,
     476                'error'   => false
     477        ) );
     478}
     479
     480/**
     481 * Get the avatar storage directory for use during registration.
     482 *
     483 * @since BuddyPress (2.2.0)
     484 *
     485 * @return string|bool Directory path on success, false on failure.
     486 */
     487function bp_attachments_signup_avatar_upload_dir() {
     488        $bp = buddypress();
     489
     490        if ( empty( $bp->signup->avatar_dir ) ) {
     491                return false;
     492        }
     493
     494        $path  = bp_attachments_get_upload_dir( 'avatar_user_dir' ) . '/signups/' . $bp->signup->avatar_dir;
     495        $newbdir = $path;
     496
     497        if ( ! file_exists( $path ) ) {
     498                @wp_mkdir_p( $path );
     499        }
     500
     501        $newurl = bp_attachments_get_upload_dir( 'avatar_user_url' ) . '/signups/' . $bp->signup->avatar_dir;
     502        $newburl = $newurl;
     503
     504        return apply_filters( 'bp_core_signup_avatar_upload_dir', array(
     505                'path'    => $path,
     506                'url'     => $newurl,
     507                'subdir'  => false,
     508                'basedir' => $newbdir,
     509                'baseurl' => $newburl,
     510                'error' => false
     511        ) );
     512}
     513
     514/**
     515 * Prepare an avatar to be displayed in the BP Attachments Editor
     516 *
     517 * @since BuddyPress (2.2.0)
     518 */
     519function bp_attachments_prepare_avatar_for_js( $avatar = null ) {
     520        if ( empty( $avatar ) ) {
     521                return;
     522        }
     523
     524        $response = array(
     525                'id'        => $avatar->item_id,
     526                'title'     => $avatar->name,
     527                'component' => $avatar->component,
     528                'filename'  => wp_basename( $avatar->dir ),
     529                'url'       => $avatar->url,
     530                'path'      => $avatar->dir,
     531                'width'     => $avatar->width,
     532                'height'    => $avatar->height,
     533                'src'       => $avatar->src,
     534        );
     535
     536        return apply_filters( 'bp_attachments_prepare_avatar_for_js', $response, $avatar );
     537}
     538
     539/**
     540 * Handle avatar uploading.
     541 *
     542 * The functions starts off by checking that the file has been uploaded
     543 * properly using bp_attachments_check_avatar_upload(). It then checks that the file
     544 * size is within limits, and that it has an accepted file extension (jpg, gif,
     545 * png). If everything checks out, crop the image and move it to its real
     546 * location.
     547 *
     548 * @since BuddyPress (2.2.0)
     549 *
     550 * @see bp_attachments_check_avatar_upload()
     551 * @see bp_attachments_check_avatar_type()
     552 *
     553 * @param array  $file The appropriate entry the from $_FILES superglobal.
     554 * @param string $component the component for the avatar (xprofile, groups,blogs..).
     555 * @return string json object or url to the uploaded avatar
     556 */
     557function bp_attachments_avatar_handle_upload( $file, $component, $item_id = 0, $do_js = false ) {
     558        $bp = buddypress();
     559
     560        /***
     561         * You may want to hook into this filter if you want to override this function.
     562         * Make sure you return false.
     563         */
     564        if ( ! apply_filters( 'bp_core_pre_avatar_handle_upload', true, $file, $component . '_avatar_upload_dir', $component, $item_id, $do_js ) ) {
     565                return true;
     566        }
     567
     568        /**
     569         * Map Dirs for backcompat
     570         */
     571        if ( empty( $bp->active_components[ $component ] ) ) {
     572                $dirs = array(
     573                        'group-avatars'  => $bp->groups->id,
     574                        'blog-avatars'   => $bp->blogs->id,
     575                        'avatars/signup' => 'signup',
     576                        'avatars'        => $bp->profile->id,
     577                );
     578
     579                if ( ! empty( $dirs[ $component ] ) ) {
     580                        $component = $dirs[ $component ];
     581                } else if ( ! empty( $bp->signup->avatar_dir ) ) {
     582                        $component = 'signup';
     583                }
     584        }
     585
     586        require_once( ABSPATH . '/wp-admin/includes/file.php' );
     587
     588        $uploadErrors = array(
     589                0 => __( 'The image was uploaded successfully', 'buddypress' ),
     590                1 => __( 'The image exceeds the maximum allowed file size of: ', 'buddypress' ) . size_format( bp_attachments_avatar_original_max_filesize() ),
     591                2 => __( 'The image exceeds the maximum allowed file size of: ', 'buddypress' ) . size_format( bp_attachments_avatar_original_max_filesize() ),
     592                3 => __( 'The uploaded file was only partially uploaded.', 'buddypress' ),
     593                4 => __( 'The image was not uploaded.', 'buddypress' ),
     594                6 => __( 'Missing a temporary folder.', 'buddypress' )
     595        );
     596
     597        if ( ! bp_attachments_check_avatar_upload( $file ) ) {
     598                return new WP_Error( 'upload_error', sprintf( __( 'Your upload failed, please try again. Error was: %s', 'buddypress' ), $uploadErrors[$file['file']['error']] ), $file );
     599        }
     600
     601        if ( ! bp_attachments_check_avatar_size( $file ) ) {
     602                return new WP_Error( 'upload_error', sprintf( __( 'The file you uploaded is too big. Please upload a file under %s', 'buddypress' ), size_format( bp_attachments_avatar_original_max_filesize() ) ), $file );
     603        }
     604
     605        if ( ! bp_attachments_check_avatar_type( $file ) ) {
     606                return new WP_Error( 'upload_error', __( 'Please upload only JPG, GIF or PNG photos.', 'buddypress' ), $file );
     607        }
     608
     609        if ( ! isset( $bp->avatar_admin ) ) {
     610                $bp->avatar_admin = new stdClass();
     611        }
     612
     613        // Set the item id and the component of the uploaded avatar
     614        $bp->avatar_admin->image = (object) array(
     615                'item_id'   => $item_id,
     616                'component' => $component,
     617        );
     618
     619        if ( ! is_callable( "bp_attachments_{$component}_avatar_upload_dir" ) ) {
     620                return new WP_Error( 'upload_error', sprintf( __( 'Avatar cannot be uploaded for the component: %s. Please contact the administrator.', 'buddypress' ), $component ), $file );
     621        }
     622
     623        // Filter the upload location
     624        add_filter( 'upload_dir', "bp_attachments_{$component}_avatar_upload_dir", 10, 0 );
     625
     626        $bp->avatar_admin->original = wp_handle_upload( $file['file'], array( 'action'=> 'bp_avatar_upload' ) );
     627
     628        // Remove the upload_dir filter, so that other upload URLs on the page
     629        // don't break
     630        remove_filter( 'upload_dir',"bp_attachments_{$component}_avatar_upload_dir", 10, 0 );
     631
     632        // Move the file to the correct upload location.
     633        if ( ! empty( $bp->avatar_admin->original['error'] ) ) {
     634                return new WP_Error( 'upload_error', sprintf( __( 'Upload Failed! Error was: %s', 'buddypress' ), $bp->avatar_admin->original['error'] ), $file );
     635        }
     636
     637        // Get image size
     638        $size  = @getimagesize( $bp->avatar_admin->original['file'] );
     639        $error = false;
     640
     641        // Check image size and shrink if too large
     642        if ( $size[0] > bp_attachments_avatar_original_max_width() ) {
     643                $editor = wp_get_image_editor( $bp->avatar_admin->original['file'] );
     644
     645                if ( ! is_wp_error( $editor ) ) {
     646                        $editor->set_quality( 100 );
     647
     648                        $resized = $editor->resize( bp_attachments_avatar_original_max_width(), bp_attachments_avatar_original_max_width(), false );
     649                        if ( ! is_wp_error( $resized ) ) {
     650                                $thumb = $editor->save( $editor->generate_filename() );
     651                        } else {
     652                                $error = $resized;
     653                        }
     654
     655                        // Check for thumbnail creation errors
     656                        if ( false === $error && is_wp_error( $thumb ) ) {
     657                                $error = $thumb;
     658                        }
     659
     660                        // Thumbnail is good so proceed
     661                        if ( false === $error ) {
     662                                $bp->avatar_admin->resized = $thumb;
     663                        }
     664
     665                } else {
     666                        $error = $editor;
     667                }
     668
     669                if ( false !== $error ) {
     670                        return $error;
     671                }
     672        }
     673
     674        // We only want to handle one image after resize.
     675        if ( empty( $bp->avatar_admin->resized ) ) {
     676                $bp->avatar_admin->image->dir = str_replace( bp_attachments_get_upload_dir(), '', $bp->avatar_admin->original['file'] );
     677        } else {
     678                $size = @getimagesize( $bp->avatar_admin->resized['path'] );
     679                $bp->avatar_admin->image->dir = str_replace( bp_attachments_get_upload_dir(), '', $bp->avatar_admin->resized['path'] );
     680                @unlink( $bp->avatar_admin->original['file'] );
     681        }
     682
     683        // Check for WP_Error on what should be an image
     684        if ( is_wp_error( $bp->avatar_admin->image->dir ) ) {
     685                $bp->avatar_admin->image->dir->add_data( sprintf( __( 'Upload failed! Error was: %s', 'buddypress' ), $bp->avatar_admin->image->dir->get_error_message() ) );
     686                return $bp->avatar_admin->image->dir;
     687        }
     688
     689        // If the uploaded image is smaller than the "full" dimensions, throw
     690        // a warning
     691        $uploaded_image = @getimagesize( bp_attachments_get_upload_dir() . $bp->avatar_admin->image->dir );
     692        $full_width     = bp_core_avatar_full_width();
     693        $full_height    = bp_core_avatar_full_height();
     694
     695        if ( isset( $uploaded_image[0] ) && $uploaded_image[0] < $full_width || $uploaded_image[1] < $full_height ) {
     696                // Remove the image that doesn't match the minimum dimensions.
     697                @unlink( $bp->avatar_admin->original['file'] );
     698
     699                // Return the error
     700                return new WP_Error( 'upload_error', sprintf( __( 'You have selected an image that is smaller than recommended. For best results, upload a picture larger than %d x %d pixels.', 'buddypress' ), $full_width, $full_height ), $file );
     701        }
     702
     703        // Set the name of the file
     704        $name = $file['file']['name'];
     705        $name_parts = pathinfo( $name );
     706        $name = trim( substr( $name, 0, - ( 1 + strlen( $name_parts['extension'] ) ) ) );
     707        $bp->avatar_admin->image->name = $name;
     708
     709        if ( ! empty( $size ) ) {
     710                $bp->avatar_admin->image->width = $size[0];
     711                $bp->avatar_admin->image->height = $size[1];
     712        }
     713
     714        // Set the url value for the image
     715        $bp->avatar_admin->image->url = bp_attachments_get_upload_dir( 'url' ) . $bp->avatar_admin->image->dir;
     716        $bp->avatar_admin->image->src = apply_filters( 'bp_get_avatar_to_crop_src', str_replace( WP_CONTENT_DIR, '', $bp->avatar_admin->image->dir ) );
     717
     718        if ( ! empty( $do_js ) ) {
     719                return bp_attachments_prepare_avatar_for_js( $bp->avatar_admin->image );
     720        } else {
     721                return $bp->avatar_admin->image->src;
     722        }
     723}
     724
     725/**
     726 * Reset the week parameter of the WordPress main query if needed
     727 *
     728 * When cropping an avatar, a $_POST['w'] var is sent, setting the 'week'
     729 * paramater of the WordPress main query to this posted var. To avoid
     730 * notices, we need to make sure this 'week' query var is reset to 0
     731 *
     732 * @since  BuddyPress (2.2.0)
     733 *
     734 * @param  WP_Quert $posts_query the main query object
     735 * @uses   bp_is_group_create()
     736 * @uses   bp_is_group_admin_page()
     737 * @uses   bp_is_group_admin_screen() to check for a group admin screen
     738 * @uses   bp_action_variable() to check for the group's avatar creation step
     739 * @uses   bp_is_user_change_avatar() to check for the user's change profile screen
     740 */
     741function bp_attachments_avatar_reset_query( $posts_query = null ) {
     742        $reset_w = false;
     743
     744        // Group's avatar edit screen
     745        if ( bp_is_group_admin_page() ) {
     746                $reset_w = bp_is_group_admin_screen( 'group-avatar' );
     747
     748        // Group's avatar create screen
     749        } else if ( bp_is_group_create() ) {
     750                /**
     751                 * we can't use bp_get_groups_current_create_step()
     752                 * as it's not set yet
     753                 */
     754                $reset_w = 'group-avatar' === bp_action_variable( 1 );
     755
     756        // User's change avatar screen
     757        } else {
     758                $reset_w = bp_is_user_change_avatar();
     759        }
     760
     761        // A user or a group is cropping an avatar
     762        if ( true === $reset_w && isset( $_POST['avatar-crop-submit'] ) ) {
     763                $posts_query->set( 'w', 0 );
     764        }
     765}
     766add_action( 'bp_parse_query', 'bp_attachments_avatar_reset_query', 10, 1 );
     767
     768/**
     769 * Crop an uploaded avatar.
     770 *
     771 * $args has the following parameters:
     772 *  object - What component the avatar is for, e.g. "user"
     773 *  avatar_dir  The absolute path to the avatar
     774 *  item_id - Item ID
     775 *  original_file - The absolute path to the original avatar file
     776 *  crop_w - Crop width
     777 *  crop_h - Crop height
     778 *  crop_x - The horizontal starting point of the crop
     779 *  crop_y - The vertical starting point of the crop
     780 *
     781 * @since BuddyPress (2.2.0)
     782 *
     783 * @param array $args {
     784 *     Array of function parameters.
     785 *     @type string $object Object type of the item whose avatar you're
     786 *           handling. 'user', 'group', 'blog', or custom. Default: 'user'.
     787 *     @type string $avatar_dir Subdirectory where avatar should be stored.
     788 *           Default: 'avatars'.
     789 *     @type bool|int $item_id ID of the item that the avatar belongs to.
     790 *     @type bool|string $original_file Absolute papth to the original avatar
     791 *           file.
     792 *     @type int $crop_w Crop width. Default: the global 'full' avatar width,
     793 *           as retrieved by bp_core_avatar_full_width().
     794 *     @type int $crop_h Crop height. Default: the global 'full' avatar height,
     795 *           as retrieved by bp_core_avatar_full_height().
     796 *     @type int $crop_x The horizontal starting point of the crop. Default: 0.
     797 *     @type int $crop_y The vertical starting point of the crop. Default: 0.
     798 * }
     799 * @return bool True on success, false on failure.
     800 */
     801function bp_attachments_avatar_handle_crop( $args = '' ) {
     802
     803        $r = wp_parse_args( $args, array(
     804                'object'        => 'user',
     805                'avatar_dir'    => 'avatar_user_dir',
     806                'item_id'       => false,
     807                'original_file' => false,
     808                'crop_w'        => bp_core_avatar_full_width(),
     809                'crop_h'        => bp_core_avatar_full_height(),
     810                'crop_x'        => 0,
     811                'crop_y'        => 0
     812        ) );
     813
     814        /***
     815         * You may want to hook into this filter if you want to override this function.
     816         * Make sure you return false.
     817         */
     818        if ( ! apply_filters( 'bp_core_pre_avatar_handle_crop', true, $r ) ) {
     819                return true;
     820        }
     821
     822        if ( empty( $r['original_file'] ) ) {
     823                return false;
     824        }
     825
     826        $original_file = bp_attachments_get_upload_dir() . $r['original_file'];
     827
     828        if ( ! file_exists( $original_file ) ) {
     829                return false;
     830        }
     831
     832        $avatar_folder_dir = bp_attachments_get_upload_dir( $r['avatar_dir'] );
     833
     834        // Backcompat
     835        if ( empty( $avatar_folder_dir ) ) {
     836                $avatar_folder_dir = bp_attachments_get_upload_dir() . '/' . $r['avatar_dir'];
     837        }
     838
     839        if ( empty( $r['item_id'] ) ) {
     840                $avatar_folder_dir = apply_filters( 'bp_core_avatar_folder_dir', dirname( $original_file ), $r['item_id'], $r['object'], $r['avatar_dir'] );
     841        } else {
     842                $avatar_folder_dir = apply_filters( 'bp_core_avatar_folder_dir',  trailingslashit( $avatar_folder_dir ) . $r['item_id'], $r['item_id'], $r['object'], $r['avatar_dir'] );
     843        }
     844
     845        if ( ! file_exists( $avatar_folder_dir ) ) {
     846                return false;
     847        }
     848
     849        require_once( ABSPATH . '/wp-admin/includes/image.php' );
     850        require_once( ABSPATH . '/wp-admin/includes/file.php' );
     851
     852        // Delete the existing avatar files for the object
     853        $existing_avatar = bp_core_fetch_avatar( array(
     854                'object'  => $r['object'],
     855                'item_id' => $r['item_id'],
     856                'html' => false,
     857        ) );
     858
     859        if ( ! empty( $existing_avatar ) ) {
     860                // Check that the new avatar doesn't have the same name as the
     861                // old one before deleting
     862                $upload_dir           = wp_upload_dir();
     863                $existing_avatar_path = str_replace( $upload_dir['baseurl'], '', $existing_avatar );
     864                $new_avatar_path      = str_replace( $upload_dir['basedir'], '', $original_file );
     865
     866                if ( $existing_avatar_path !== $new_avatar_path ) {
     867                        bp_attachments_delete_existing_avatar( array( 'object' => $r['object'], 'item_id' => $r['item_id'], 'avatar_path' => $avatar_folder_dir ) );
     868                }
     869        }
     870
     871        // Make sure we at least have a width and height for cropping
     872        if ( empty( $r['crop_w'] ) ) {
     873                $r['crop_w'] = bp_core_avatar_full_width();
     874        }
     875
     876        if ( empty( $r['crop_h'] ) ) {
     877                $r['crop_h'] = bp_core_avatar_full_height();
     878        }
     879
     880        // Get the file extension
     881        $data = @getimagesize( $original_file );
     882        $ext  = $data['mime'] == 'image/png' ? 'png' : 'jpg';
     883
     884        // Set the full and thumb filenames
     885        $full_filename  = wp_hash( $original_file . time() ) . '-bpfull.'  . $ext;
     886        $thumb_filename = wp_hash( $original_file . time() ) . '-bpthumb.' . $ext;
     887
     888        // Crop the image
     889        $full_cropped  = wp_crop_image( $original_file, (int) $r['crop_x'], (int) $r['crop_y'], (int) $r['crop_w'], (int) $r['crop_h'], bp_core_avatar_full_width(),  bp_core_avatar_full_height(),  false, $avatar_folder_dir . '/' . $full_filename  );
     890        $thumb_cropped = wp_crop_image( $original_file, (int) $r['crop_x'], (int) $r['crop_y'], (int) $r['crop_w'], (int) $r['crop_h'], bp_core_avatar_thumb_width(), bp_core_avatar_thumb_height(), false, $avatar_folder_dir . '/' . $thumb_filename );
     891
     892        // Check for errors
     893        if ( empty( $full_cropped ) || empty( $thumb_cropped ) || is_wp_error( $full_cropped ) || is_wp_error( $thumb_cropped ) ) {
     894                return false;
     895        }
     896
     897        // Remove the original
     898        @unlink( $original_file );
     899
     900        return true;
     901}
     902
     903/**
     904 * Is the current avatar upload error-free?
     905 *
     906 * @since BuddyPress (2.2.0)
     907 *
     908 * @param array $file The $_FILES array.
     909 * @return bool True if no errors are found. False if there are errors.
     910 */
     911function bp_attachments_check_avatar_upload( $file ) {
     912        if ( isset( $file['error'] ) && $file['error'] )
     913                return false;
     914
     915        return true;
     916}
     917
     918/**
     919 * Is the file size of the current avatar upload permitted?
     920 *
     921 * @since BuddyPress (2.2.0)
     922 *
     923 * @param array $file The $_FILES array.
     924 * @return bool True if the avatar is under the size limit, otherwise false.
     925 */
     926function bp_attachments_check_avatar_size( $file ) {
     927        if ( $file['file']['size'] > bp_attachments_avatar_original_max_filesize() )
     928                return false;
     929
     930        return true;
     931}
     932
     933/**
     934 * Does the current avatar upload have an allowed file type?
     935 *
     936 * Permitted file types are JPG, GIF and PNG.
     937 *
     938 * @since BuddyPress (2.2.0)
     939 *
     940 * @param array $file The $_FILES array.
     941 * @return bool True if the file extension is permitted, otherwise false.
     942 */
     943function bp_attachments_check_avatar_type( $file ) {
     944        if ( ( !empty( $file['file']['type'] ) && !preg_match('/(jpe?g|gif|png)$/i', $file['file']['type'] ) ) || !preg_match( '/(jpe?g|gif|png)$/i', $file['file']['name'] ) )
     945                return false;
     946
     947        return true;
     948}
     949
     950/**
     951 * Get the max width for original avatar uploads.
     952 *
     953 * @since BuddyPress (2.2.0)
     954 *
     955 * @return int The max width for original avatar uploads.
     956 */
     957function bp_attachments_avatar_original_max_width() {
     958        return apply_filters( 'bp_core_avatar_original_max_width', (int) buddypress()->avatar->original_max_width );
     959}
     960
     961/**
     962 * Get the max filesize for original avatar uploads.
     963 *
     964 * @since BuddyPress (2.2.0)
     965 *
     966 * @return int The max filesize for original avatar uploads.
     967 */
     968function bp_attachments_avatar_original_max_filesize() {
     969        return apply_filters( 'bp_core_avatar_original_max_filesize', (int) buddypress()->avatar->original_max_filesize );
     970}
     971
     972/** Attachments **************************************************************/
     973
     974/**
     975 * Get the term id for a given component
     976 *
     977 * Using term slugs, there's a possibility $bp->{component}->id != term slug
     978 *
     979 * @since BuddyPress (2.2.0)
     980 *
     981 * @param  string $component_id the component id
     982 * @return int    the term id
     983 */
     984function bp_attachments_get_component_term_id( $component_id = '' ) {
     985        $bp = buddypress();
     986
     987        if ( ! empty( $bp->attachments->component_terms[ $component_id ] ) ) {
     988                return (int) $bp->attachments->component_terms[ $component_id ];
     989        }
     990
     991        return false;
     992}
     993
     994/**
     995 * Get the component id for a given term
     996 *
     997 * Using term slugs, there's a possibility $bp->{component}->id != term slug
     998 *
     999 * @since BuddyPress (2.2.0)
     1000 *
     1001 * @param  int $term_id the term id
     1002 * @return string       the component id
     1003 */
     1004function bp_attachments_get_term_component_id( $term_id = 0 ) {
     1005        $bp = buddypress();
     1006
     1007        $terms_component = array_flip( $bp->attachments->component_terms );
     1008
     1009        if ( ! empty( $terms_component[ $term_id ] ) ) {
     1010                return $terms_component[ $term_id ];
     1011        }
     1012
     1013        return false;
     1014}
     1015
     1016/**
     1017 * Get a single attachment
     1018 *
     1019 * @since BuddyPress (2.2.0)
     1020 *
     1021 * @param  integer $attachment_id
     1022 * @return BP_Attachments
     1023 */
     1024function bp_attachments_get_attachment( $attachment_id = 0 ) {
     1025        if ( empty( $attachment_id ) )
     1026                return false;
     1027
     1028        $attachment = new BP_Attachments( $attachment_id );
     1029
     1030        return apply_filters( 'bp_attachments_get_attachment', $attachment );
     1031}
     1032
     1033/**
     1034 * Get a single attachment
     1035 *
     1036 * @since BuddyPress (2.2.0)
     1037 *
     1038 * @param  array $args
     1039 * @return array of BP_Attachments
     1040 */
     1041function bp_attachments_get_attachments( $args = array() ) {
     1042
     1043        $defaults = array(
     1044                'item_ids'        => array(), // one or more item ids regarding the component (eg group_ids, message_ids )
     1045                'component'           => false,   // groups / messages / blogs / xprofile...
     1046                'show_private'    => false,   // wether to include private attachment
     1047                'user_id'             => false,   // the author id of the attachment
     1048                'per_page'            => 20,
     1049                'page'                => 1,
     1050                'search'          => false,
     1051                'exclude'                 => false,   // comma separated list or array of attachment ids.
     1052                'orderby'                 => 'ID',
     1053                'order'           => 'DESC',
     1054        );
     1055
     1056        $r = bp_parse_args( $args, $defaults, 'attachments_get_args' );
     1057
     1058        $attachments = wp_cache_get( 'bp_attachments_attachments', 'bp' );
     1059
     1060        if ( empty( $attachments ) ) {
     1061                $attachments = BP_Attachments::get( array(
     1062                        'item_ids'        => (array) $r['item_ids'],
     1063                        'component'           => $r['component'],
     1064                        'show_private'    => (bool) $r['show_private'],
     1065                        'user_id'             => $r['user_id'],
     1066                        'per_page'            => $r['per_page'],
     1067                        'page'                => $r['page'],
     1068                        'search'          => $r['search'],
     1069                        'exclude'                 => $r['exclude'],
     1070                        'orderby'                 => $r['orderby'],
     1071                        'order'           => $r['order'],
     1072                ) );
     1073
     1074                wp_cache_set( 'bp_attachments_attachments', $attachments, 'bp' );
     1075        }
     1076
     1077        return apply_filters_ref_array( 'bp_attachments_get_attachments', array( &$attachments, &$r ) );
     1078}
     1079
     1080/**
     1081 * Upload attachment
     1082 *
     1083 * @since BuddyPress (2.2.0)
     1084 *
     1085 * @param  array $args
     1086 * @return int the id of the created attachment
     1087 */
     1088function bp_attachments_handle_upload( $args = array() ) {
     1089
     1090        $r = bp_parse_args( $args, array(
     1091                'item_id'         => 0,
     1092                'component'       => '',
     1093                'item_type'       => 'attachment',
     1094                'action'          => 'bp_attachments_upload',
     1095                'file_id'         => 'bp_attachment_file',
     1096        ), 'attachments_handle_upload_args' );
     1097
     1098        $attachment_upload = BP_Attachments_Upload::start( $r );
     1099
     1100        return $attachment_upload->attachment_id;
     1101}
     1102
     1103/**
     1104 * Delete attachment
     1105 *
     1106 * @since BuddyPress (2.2.0)
     1107 *
     1108 * @param  int the id of the attachment
     1109 * @return mixed false/title of the deleted attachment
     1110 */
     1111function bp_attachments_delete_attachment( $attachment_id = 0 ) {
     1112        if ( empty( $attachment_id ) ) {
     1113                return false;
     1114        }
     1115
     1116        if ( ! bp_attachments_current_user_can( 'delete_bp_attachment', $attachment_id ) ) {
     1117                return false;
     1118        }
     1119
     1120        $deleted = BP_Attachments::delete( $attachment_id );
     1121
     1122        if ( ! empty( $deleted->post_title ) ) {
     1123                return $deleted->post_title;
     1124        } else {
     1125                return false;
     1126        }
     1127}
     1128
     1129/**
     1130 * Delete attachment
     1131 *
     1132 * @since BuddyPress (2.2.0)
     1133 *
     1134 * @param  array $args
     1135 * @return mixed false/title of the updated attachment
     1136 */
     1137function bp_attachments_update_attachment( $args = array() ) {
     1138        $r = bp_parse_args( $args, array(
     1139                'id'          => 0,
     1140                'title'       => '',
     1141                'description' => '',
     1142                'privacy'     => 'inherit',
     1143                'component'   => array(),
     1144                'terms'       => '',
     1145                'ajax'        => false
     1146        ), 'attachments_update_attachment_args' );
     1147
     1148        if ( empty( $r['id'] ) ) {
     1149                return false;
     1150        }
     1151
     1152        if ( ! bp_attachments_current_user_can( 'edit_bp_attachment', $r['id'] ) ) {
     1153                return false;
     1154        }
     1155
     1156        $attachment = new BP_Attachments( $r['id'] );
     1157
     1158        if ( empty( $attachment ) ) {
     1159                return false;
     1160        }
     1161
     1162        if ( empty( $r['title'] ) ) {
     1163                $r['title'] = $attachment->title;
     1164        }
     1165
     1166        $attachment->title       = $r['title'];
     1167        $attachment->description = $r['description'];
     1168        $attachment->status      = $r['privacy'];
     1169
     1170        if ( empty( $r['ajax'] ) ) {
     1171
     1172                $prev_item_ids = ! empty( $attachment->item_ids ) ? $attachment->item_ids : false;
     1173
     1174                if ( ! empty( $r['terms'] ) ) {
     1175                        $terms = explode( ',', $r['terms'] );
     1176                        // Let's handle the item ids here
     1177                        foreach ( $terms as $key => $term ) {
     1178                                if ( empty( $r['component'][ $term ] ) ) {
     1179                                        // delete all !
     1180                                        delete_post_meta( $id, "_bp_{$term}_id" );
     1181                                        unset( $terms[ $key ] );
     1182                                } else {
     1183                                        if ( empty( $prev_item_ids->{$term} ) ) {
     1184                                                foreach( $r['component'][ $term ] as $item_id )
     1185                                                        add_post_meta( $id, "_bp_{$term}_id", $item_id );
     1186
     1187                                        } else {
     1188                                                $to_delete = array_diff( $prev_item_ids->{$term}, $r['component'][ $term ] );
     1189                                                $to_add    = array_diff( $r['component'][ $term ], $prev_item_ids->{$term} );
     1190
     1191                                                if ( ! empty( $to_delete ) ){
     1192                                                        // Delete item ids
     1193                                                        foreach ( $to_delete as $item_id ) {
     1194                                                                delete_post_meta( $id, "_bp_{$term}_id", $item_id );
     1195                                                        }
     1196                                                }
     1197
     1198                                                if ( ! empty( $to_add ) ){
     1199                                                        // Delete item ids
     1200                                                        foreach ( $to_add as $item_id ) {
     1201                                                                add_post_meta( $id, "_bp_{$term}_id", $item_id );
     1202                                                        }
     1203                                                }
     1204                                        }
     1205                                }
     1206                        }
     1207
     1208                        if ( empty( $terms ) ) {
     1209                                $terms = null;
     1210                        }
     1211
     1212                        /**
     1213                         * @todo transform slugs into ids using
     1214                         * bp_attachments_get_component_term_id( $component_id )
     1215                         */
     1216                        wp_set_object_terms( $r['id'], $terms, 'bp_component' );
     1217
     1218                } else {
     1219                        wp_set_object_terms( $r['id'], null, 'bp_component' );
     1220                }
     1221
     1222        }
     1223
     1224        return $attachment->update();
     1225}
     1226
     1227/**
     1228 * Launch the BP Attachments Editor
     1229 *
     1230 * @since BuddyPress (2.2.0)
     1231 */
     1232function bp_attachments_browser( $browser_id, $settings = array() ) {
     1233        BP_Attachments_Browser::browser( $browser_id, $settings );
     1234}
     1235
     1236/**
     1237 * Retrieve the URL for an attachment.
     1238 *
     1239 * This is an adapted version of wp_get_attachment_url()
     1240 *
     1241 * @since BuddyPress (2.2.0)
     1242 *
     1243 * @param int $post_id Attachment ID.
     1244 * @return string
     1245 */
     1246function bp_attachments_get_attachment_url( $post_id = 0 ) {
     1247        $post_id = (int) $post_id;
     1248        $post = get_post( $post_id );
     1249
     1250        if ( empty( $post ) ) {
     1251                return false;
     1252        }
     1253
     1254        if ( 'bp_attachment' != $post->post_type ) {
     1255                return false;
     1256        }
     1257
     1258        $url = '';
     1259        // Get attached file
     1260        $file = get_post_meta( $post->ID, '_wp_attached_file', true );
     1261        if ( ! empty( $file ) ) {
     1262                $uploads = wp_upload_dir();
     1263
     1264                // Get upload directory
     1265                if ( ! empty( $uploads ) && false === $uploads['error'] ) {
     1266
     1267                        // Check that the upload base exists in the file location
     1268                        if ( 0 === strpos( $file, $uploads['basedir'] ) ) {
     1269                                // replace file location with url location
     1270                                $url = str_replace( $uploads['basedir'], $uploads['baseurl'], $file );
     1271                        } else if ( false !== strpos( $file, 'wp-content/uploads' ) ) {
     1272                                $url = $uploads['baseurl'] . substr( $file, strpos($file, 'wp-content/uploads') + 18 );
     1273
     1274                        // Its a newly uploaded file, therefor $file is relative to the basedir.
     1275                        } else {
     1276                                $url = $uploads['baseurl'] . "/$file";
     1277                        }
     1278                }
     1279        }
     1280
     1281        //If any of the above options failed, Fallback on the GUID
     1282        if ( empty( $url ) ) {
     1283                $url = get_the_guid( $post->ID );
     1284        }
     1285
     1286        $url = apply_filters( 'bp_attachments_get_attachment_url', $url, $post->ID );
     1287
     1288        if ( empty( $url ) ) {
     1289                return false;
     1290        }
     1291
     1292        return $url;
     1293}
     1294
     1295/**
     1296 * Custom filter for WordPress image_downsize
     1297 *
     1298 * Use it before and after wp_get_attachment_image()
     1299 *
     1300 * @see bp_attachments_get_attachment_image()
     1301 *
     1302 * @since BuddyPress (2.2.0)
     1303 */
     1304function bp_attachments_image_downsize( $output = '', $id = 0, $size = 'medium' ) {
     1305        $img_url = bp_attachments_get_attachment_url( $id );
     1306        $meta = wp_get_attachment_metadata($id);
     1307        $width = $height = 0;
     1308        $is_intermediate = false;
     1309        $img_url_basename = wp_basename($img_url);
     1310
     1311        // try for a new style intermediate size
     1312        if ( $intermediate = image_get_intermediate_size($id, $size) ) {
     1313                $img_url = str_replace($img_url_basename, $intermediate['file'], $img_url);
     1314                $width = $intermediate['width'];
     1315                $height = $intermediate['height'];
     1316                $is_intermediate = true;
     1317        }
     1318        elseif ( $size == 'thumbnail' ) {
     1319                // fall back to the old thumbnail
     1320                if ( ($thumb_file = wp_get_attachment_thumb_file($id)) && $info = getimagesize($thumb_file) ) {
     1321                        $img_url = str_replace($img_url_basename, wp_basename($thumb_file), $img_url);
     1322                        $width = $info[0];
     1323                        $height = $info[1];
     1324                        $is_intermediate = true;
     1325                }
     1326        }
     1327        if ( !$width && !$height && isset( $meta['width'], $meta['height'] ) ) {
     1328                // any other type: use the real image
     1329                $width = $meta['width'];
     1330                $height = $meta['height'];
     1331        }
     1332
     1333        if ( $img_url) {
     1334                // we have the actual image size, but might need to further constrain it if content_width is narrower
     1335                list( $width, $height ) = image_constrain_size_for_editor( $width, $height, $size );
     1336
     1337                return array( $img_url, $width, $height, $is_intermediate );
     1338        }
     1339        return false;
     1340}
     1341
     1342/**
     1343 * Get an HTML img element representing an image attachment
     1344 *
     1345 * @since BuddyPress (2.2.0)
     1346 *
     1347 * @param  int          $attachment_id Image attachment ID.
     1348 * @param  string|array $size          Optional. Default 'thumbnail'.
     1349 * @param  bool         $icon          Optional. Whether it is an icon. Default false.
     1350 * @param  string|array $attr          Optional. Attributes for the image markup. Default empty string.
     1351 * @uses   wp_get_attachment_image()
     1352 * @return string HTML img element or empty string on failure.
     1353 */
     1354function bp_attachments_get_attachment_image( $attachment_id, $size = 'thumbnail', $icon = false, $attr = '' ) {
     1355        // Temporarly filter image_downsize()
     1356        add_filter( 'image_downsize', 'bp_attachments_image_downsize', 10, 3 );
     1357
     1358        $attachment_image = wp_get_attachment_image( $attachment_id, $size, $icon, $attr );
     1359
     1360        // Reset image_downsize()
     1361        remove_filter( 'image_downsize', 'bp_attachments_image_downsize', 10, 3 );
     1362
     1363        return apply_filters( 'bp_attachments_get_attachment_image', $attachment_image, $attachment_id, $size, $icon, $attr );
     1364}
     1365
     1366/**
     1367 * Prepare an attachment to be displayed in the BP Attachments Editor
     1368 *
     1369 * this is an adapted copy of wp_prepare_attachment_for_js()
     1370 *
     1371 * @since BuddyPress (2.2.0)
     1372 */
     1373function bp_attachments_prepare_attachment_for_js( $attachment ) {
     1374        if ( empty( $attachment ) ) {
     1375                return;
     1376        }
     1377
     1378        if ( ! is_a( $attachment, 'WP_Post' ) && is_numeric( $attachment ) ) {
     1379                $get_attachment = bp_attachments_get_attachment( $attachment );
     1380
     1381                if ( ! empty( $get_attachment->attachment ) ) {
     1382                        $attachment = $get_attachment->attachment;
     1383                }
     1384        }
     1385
     1386        if ( 'bp_attachment' != $attachment->post_type ) {
     1387                return;
     1388        }
     1389
     1390        $meta = wp_get_attachment_metadata( $attachment->ID );
     1391        if ( false !== strpos( $attachment->post_mime_type, '/' ) ) {
     1392                list( $type, $subtype ) = explode( '/', $attachment->post_mime_type );
     1393        } else {
     1394                list( $type, $subtype ) = array( $attachment->post_mime_type, '' );
     1395        }
     1396
     1397        $attachment_url = bp_attachments_get_attachment_url( $attachment->ID );
     1398
     1399        $response = array(
     1400                'id'            => $attachment->ID,
     1401                'title'         => $attachment->post_title,
     1402                'filename'      => wp_basename( $attachment->guid ),
     1403                'url'           => $attachment_url,
     1404                'link'          => get_attachment_link( $attachment->ID ),
     1405                'alt'           => get_post_meta( $attachment->ID, '_wp_attachment_image_alt', true ),
     1406                'author'        => $attachment->post_author,
     1407                'description'   => $attachment->post_content,
     1408                'caption'       => $attachment->post_excerpt,
     1409                'name'          => $attachment->post_name,
     1410                'status'        => $attachment->post_status,
     1411                'uploadedTo'    => $attachment->post_parent,
     1412                'date'          => strtotime( $attachment->post_date_gmt ) * 1000,
     1413                'modified'      => strtotime( $attachment->post_modified_gmt ) * 1000,
     1414                'menuOrder'     => $attachment->menu_order,
     1415                'mime'          => $attachment->post_mime_type,
     1416                'type'          => $type,
     1417                'subtype'       => $subtype,
     1418                'icon'          => wp_mime_type_icon( $attachment->ID ),
     1419                'dateFormatted' => mysql2date( get_option('date_format'), $attachment->post_date ),
     1420                'nonces'        => array(
     1421                        'update'    => false,
     1422                        'delete'    => false,
     1423                ),
     1424                'editLink'      => false,
     1425        );
     1426
     1427        if ( current_user_can( 'edit_bp_attachment', $attachment->ID ) ) {
     1428                $response['nonces']['update'] = wp_create_nonce( 'update_bp_attachment_' . $attachment->ID );
     1429                $response['editLink'] = bp_attachments_get_edit_link( $attachment->ID, $attachment->post_author );
     1430        }
     1431
     1432        if ( current_user_can( 'delete_bp_attachment', $attachment->ID ) ) {
     1433                $response['nonces']['delete'] = wp_create_nonce( 'delete_bp_attachment_' . $attachment->ID );
     1434        }
     1435
     1436        if ( $meta && 'image' === $type ) {
     1437                $sizes = array();
     1438                /** This filter is documented in wp-admin/includes/media.php */
     1439                $possible_sizes = apply_filters( 'image_size_names_choose', array(
     1440                        'thumbnail' => __('Thumbnail'),
     1441                        'medium'    => __('Medium'),
     1442                        'large'     => __('Large'),
     1443                        'full'      => __('Full Size'),
     1444                ) );
     1445                unset( $possible_sizes['full'] );
     1446
     1447                // Loop through all potential sizes that may be chosen. Try to do this with some efficiency.
     1448                // First: run the image_downsize filter. If it returns something, we can use its data.
     1449                // If the filter does not return something, then image_downsize() is just an expensive
     1450                // way to check the image metadata, which we do second.
     1451                foreach ( $possible_sizes as $size => $label ) {
     1452                        if ( $downsize = apply_filters( 'image_downsize', false, $attachment->ID, $size ) ) {
     1453                                if ( ! $downsize[3] ) {
     1454                                        continue;
     1455                                }
     1456
     1457                                $sizes[ $size ] = array(
     1458                                        'height'      => $downsize[2],
     1459                                        'width'       => $downsize[1],
     1460                                        'url'         => $downsize[0],
     1461                                        'orientation' => $downsize[2] > $downsize[1] ? 'portrait' : 'landscape',
     1462                                );
     1463                        } elseif ( isset( $meta['sizes'][ $size ] ) ) {
     1464                                if ( ! isset( $base_url ) ) {
     1465                                        $base_url = str_replace( wp_basename( $attachment_url ), '', $attachment_url );
     1466                                }
     1467
     1468                                // Nothing from the filter, so consult image metadata if we have it.
     1469                                $size_meta = $meta['sizes'][ $size ];
     1470
     1471                                // We have the actual image size, but might need to further constrain it if content_width is narrower.
     1472                                // Thumbnail, medium, and full sizes are also checked against the site's height/width options.
     1473                                list( $width, $height ) = image_constrain_size_for_editor( $size_meta['width'], $size_meta['height'], $size, 'edit' );
     1474
     1475                                $sizes[ $size ] = array(
     1476                                        'height'      => $height,
     1477                                        'width'       => $width,
     1478                                        'url'         => $base_url . $size_meta['file'],
     1479                                        'orientation' => $height > $width ? 'portrait' : 'landscape',
     1480                                );
     1481                        }
     1482                }
     1483
     1484                $sizes['full'] = array( 'url' => $attachment_url );
     1485
     1486                if ( isset( $meta['height'], $meta['width'] ) ) {
     1487                        $sizes['full']['height'] = $meta['height'];
     1488                        $sizes['full']['width'] = $meta['width'];
     1489                        $sizes['full']['orientation'] = $meta['height'] > $meta['width'] ? 'portrait' : 'landscape';
     1490                }
     1491
     1492                $response = array_merge( $response, array( 'sizes' => $sizes ), $sizes['full'] );
     1493        } elseif ( $meta && 'video' === $type ) {
     1494                if ( isset( $meta['width'] ) ) {
     1495                        $response['width'] = (int) $meta['width'];
     1496                }
     1497
     1498                if ( isset( $meta['height'] ) ) {
     1499                        $response['height'] = (int) $meta['height'];
     1500                }
     1501        }
     1502
     1503        if ( $meta && ( 'audio' === $type || 'video' === $type ) ) {
     1504                if ( isset( $meta['length_formatted'] ) ) {
     1505                        $response['fileLength'] = $meta['length_formatted'];
     1506                }
     1507        }
     1508
     1509        return apply_filters( 'bp_attachments_prepare_attachment_for_js', $response, $attachment, $meta );
     1510}
     1511
     1512/**
     1513 * Build the edit link of an attachement
     1514 *
     1515 * @since BuddyPress (2.2.0)
     1516 */
     1517function bp_attachments_get_edit_link( $attachment_id = 0, $user_id = 0 ) {
     1518        if ( empty( $attachment_id ) ) {
     1519                return false;
     1520        }
     1521
     1522        if ( empty( $user_id ) ) {
     1523                $user_id = bp_loggedin_user_id();
     1524        }
     1525
     1526        $edit_link = trailingslashit( bp_core_get_user_domain( $user_id ) . buddypress()->attachments->slug );
     1527        $edit_link = add_query_arg( array( 'attachment' => $attachment_id, 'action' => 'edit' ), $edit_link );
     1528
     1529        return apply_filters( 'bp_attachments_get_edit_link', $edit_link, $attachment_id, $user_id );
     1530}
     1531
     1532/**
     1533 * Build the delete link of an attachement
     1534 *
     1535 * @since BuddyPress (2.2.0)
     1536 */
     1537function bp_attachments_get_delete_link( $attachment_id = 0, $user_id = 0 ) {
     1538        if ( empty( $attachment_id ) ) {
     1539                return false;
     1540        }
     1541
     1542        if ( empty( $user_id ) ) {
     1543                $user_id = bp_loggedin_user_id();
     1544        }
     1545
     1546        $delete_link = trailingslashit( bp_core_get_user_domain( $user_id ) . buddypress()->attachments->slug );
     1547        $delete_link = add_query_arg( array( 'attachment' => $attachment_id, 'action' => 'delete' ), $delete_link );
     1548
     1549        return apply_filters( 'bp_attachments_get_delete_link', $delete_link, $attachment_id, $user_id );
     1550}
  • src/bp-attachments/bp-attachments-loader.php

    diff --git src/bp-attachments/bp-attachments-loader.php src/bp-attachments/bp-attachments-loader.php
    index e69de29..e37024f 100644
     
     1<?php
     2/**
     3 * Attachments Component.
     4 *
     5 * A media component, for others !
     6 *
     7 * @package BuddyPress
     8 * @subpackage Attachments
     9 */
     10
     11// Exit if accessed directly
     12defined( 'ABSPATH' ) or exit;
     13/**
     14 * Main Attachments Class.
     15 *
     16 * @since BuddyPress (2.2.0)
     17 */
     18class BP_Attachments_Component extends BP_Component {
     19
     20        /**
     21         * If true enables upload dirs, post type & taxonomy
     22         *
     23         * @var bool
     24         */
     25        public $use_api;
     26
     27        /**
     28         * Start the attachments component setup process.
     29         *
     30         * @since BuddyPress (2.2.0)
     31         */
     32        public function __construct() {
     33                parent::start(
     34                        'attachments',
     35                        __( 'Attachments', 'buddypress' ),
     36                        buddypress()->plugin_dir
     37                );
     38
     39                // Filter here to enable Attachments API
     40                $this->use_api = apply_filters( 'bp_attachments_use_attachments_api', false );
     41
     42                // Launch specific hooks
     43                $this->actions();
     44        }
     45
     46        /**
     47         * Include component files.
     48         *
     49         * @since BuddyPress (2.2.0)
     50         */
     51        public function includes( $includes = array() ) {
     52                // Files to include
     53                $includes = array(
     54                        'cssjs',
     55                        'actions',
     56                        'filters',
     57                        'screens',
     58                        'caps',
     59                        'classes',
     60                        'ajax',
     61                        'functions',
     62                );
     63
     64                if ( is_admin() ) {
     65                        $includes[] = 'admin';
     66                }
     67
     68                parent::includes( $includes );
     69        }
     70
     71        /**
     72         * Set up component global variables.
     73         *
     74         * @since BuddyPress (2.2.0)
     75         */
     76        public function setup_globals( $args = array() ) {
     77                $bp = buddypress();
     78
     79                // Define a slug, if necessary
     80                if ( ! defined( 'BP_ATTACHMENTS_SLUG' ) ) {
     81                        define( 'BP_ATTACHMENTS_SLUG', $this->id );
     82                }
     83
     84                // All globals for attachments component.
     85                $args = array(
     86                        'slug'                  => BP_ATTACHMENTS_SLUG,
     87                        'has_directory'         => false,
     88                        'notification_callback' => 'bp_attachments_format_notifications',
     89                );
     90
     91                parent::setup_globals( $args );
     92        }
     93
     94        /**
     95         * Run specific actions to create post type/taxonomy...
     96         *
     97         * @since BuddyPress (2.2.0)
     98         */
     99        public function actions() {
     100                // register upload datas
     101                add_action( 'bp_' . $this->id . '_setup_globals', array( $this, 'register_upload_datas' ), 10 );
     102
     103                // Edit user's profile photo nav
     104                add_action( 'bp_xprofile_setup_nav', array( $this, 'xprofile_setup_nav' )        );
     105                add_filter( 'bp_xprofile_admin_nav', array( $this, 'xprofile_setup_nav' ), 10, 1 );
     106
     107                // Group's avatar creation step
     108                add_filter( 'groups_create_group_steps', array( $this, 'groups_setup_avatar_step' ), 10, 1 );
     109                add_action( 'bp_groups_setup_nav',       array( $this, 'groups_setup_manage_tab'  )        );
     110
     111                if ( ! empty( $this->use_api ) && get_current_blog_id() == bp_get_root_blog_id() ) {
     112                        // register bp_attachments post type
     113                        add_action( 'bp_init', array( $this, 'register_post_types' ) );
     114                        // register bp_component taxonomy
     115                        add_action( 'bp_init', array( $this, 'register_taxonomies' ) );
     116
     117                        // Eventually create/update the component terms mapping
     118                        add_action( 'bp_init', array( $this, 'component_terms' ), 999 );
     119                }
     120        }
     121
     122        /**
     123         * Set upload dirs and avatar globals
     124         *
     125         * @since BuddyPress (2.2.0)
     126         */
     127        public function register_upload_datas() {
     128                $bp = buddypress();
     129
     130                $bp->{$this->id}->use_api = $this->use_api;
     131
     132                // Set upload dirs
     133                $upload_data = bp_attachments_set_upload_dirs();
     134
     135                if( ! empty( $upload_data ) && is_array( $upload_data ) ) {
     136                        foreach ( $upload_data as $key => $data ) {
     137                                // adding the uploads dir and url to Attachments component global.
     138                                $bp->{$this->id}->{$key} = $data;
     139                        }
     140                }
     141
     142                if ( empty( $bp->avatar ) ) {
     143                        return;
     144                }
     145
     146                if ( ! defined( 'BP_AVATAR_ORIGINAL_MAX_WIDTH' ) ) {
     147                        define( 'BP_AVATAR_ORIGINAL_MAX_WIDTH', 450 );
     148                }
     149
     150                if ( ! defined( 'BP_AVATAR_ORIGINAL_MAX_FILESIZE' ) ) {
     151
     152                        if ( ! isset( $bp->site_options['fileupload_maxk'] ) ) {
     153                                define( 'BP_AVATAR_ORIGINAL_MAX_FILESIZE', 5120000 ); // 5mb
     154                        } else {
     155                                define( 'BP_AVATAR_ORIGINAL_MAX_FILESIZE', $bp->site_options['fileupload_maxk'] * 1024 );
     156                        }
     157                }
     158
     159                // Upload maximums
     160                $bp->avatar->original_max_width    = BP_AVATAR_ORIGINAL_MAX_WIDTH;
     161                $bp->avatar->original_max_filesize = BP_AVATAR_ORIGINAL_MAX_FILESIZE;
     162
     163                // These have to be set on page load in order to avoid infinite filter loops at runtime
     164                $bp->avatar->upload_path = bp_attachments_get_upload_dir();
     165                $bp->avatar->url         = bp_attachments_get_upload_dir( 'url' );
     166
     167                // Backpat for pre-1.5
     168                if ( ! defined( 'BP_AVATAR_UPLOAD_PATH' ) ) {
     169                        define( 'BP_AVATAR_UPLOAD_PATH', $bp->avatar->upload_path );
     170                }
     171
     172                // Backpat for pre-1.5
     173                if ( ! defined( 'BP_AVATAR_URL' ) ) {
     174                        define( 'BP_AVATAR_URL', $bp->avatar->url );
     175                }
     176        }
     177
     178        /**
     179         * Add the xProfile Change photo navigation
     180         *
     181         * @since BuddyPress (2.2.0)
     182         * @param array $wp_admin_nav
     183         */
     184        public function xprofile_setup_nav( $wp_admin_nav = array() ) {
     185                $bp = buddypress();
     186
     187                if ( empty( $bp->profile->slug ) || ! bp_attachments_avatar_is_enabled() ) {
     188                        return $wp_admin_nav;
     189                }
     190
     191                // Determine user to use
     192                if ( bp_displayed_user_domain() ) {
     193                        $user_domain = bp_displayed_user_domain();
     194                } elseif ( bp_loggedin_user_domain() ) {
     195                        $user_domain = bp_loggedin_user_domain();
     196                } else {
     197                        return $wp_admin_nav;
     198                }
     199
     200                $profile_link = trailingslashit( $user_domain . $bp->profile->slug );
     201
     202                if ( ! is_array( $wp_admin_nav ) ) {
     203                        bp_core_new_subnav_item( array(
     204                                'name'            => _x( 'Change Profile Photo', 'Profile header sub menu', 'buddypress' ),
     205                                'slug'            => 'change-avatar',
     206                                'parent_url'      => $profile_link,
     207                                'parent_slug'     => $bp->profile->slug,
     208                                'screen_function' => 'bp_attachments_screen_change_avatar',
     209                                'position'        => 30,
     210                                'user_has_access' => bp_core_can_edit_settings()
     211                        ) );
     212                } else {
     213                        $profile_link = trailingslashit( bp_loggedin_user_domain() . $bp->profile->slug );
     214
     215                        $wp_admin_nav[] = array(
     216                                'parent' => 'my-account-' . $bp->profile->id,
     217                                'id'     => 'my-account-' . $bp->profile->id . '-change-avatar',
     218                                'title'  => _x( 'Change Profile Photo', 'My Account Profile sub nav', 'buddypress' ),
     219                                'href'   => trailingslashit( $profile_link . 'change-avatar' )
     220                        );
     221
     222                        return $wp_admin_nav;
     223                }
     224        }
     225
     226        /**
     227         * Add the Group's avatar create step
     228         *
     229         * @since BuddyPress (2.2.0)
     230         * @param array $group_creation_steps
     231         */
     232        public function groups_setup_avatar_step( $group_creation_steps = array() ) {
     233                $bp = buddypress();
     234
     235                // If avatar uploads are not disabled, add avatar option
     236                if ( bp_attachments_avatar_is_enabled() ) {
     237                        $group_creation_steps['group-avatar'] = array(
     238                                'name'     => _x( 'Photo', 'Group screen nav', 'buddypress' ),
     239                                'position' => 20
     240                        );
     241                }
     242
     243                return $group_creation_steps;
     244        }
     245
     246        /**
     247         * Add the Group's change avatar nav
     248         *
     249         * @since BuddyPress (2.2.0)
     250         * @param array $group_creation_steps
     251         */
     252        public function groups_setup_manage_tab() {
     253                if ( ! bp_is_group() || ! bp_attachments_avatar_is_enabled() ) {
     254                        return;
     255                }
     256
     257                $group = groups_get_current_group();
     258                $admin_link = trailingslashit( bp_get_group_permalink( $group ) . 'admin' );
     259
     260                bp_core_new_subnav_item( array(
     261                        'name'              => __( 'Photo', 'buddypress' ),
     262                        'slug'              => 'group-avatar',
     263                        'parent_url'        => $admin_link,
     264                        'parent_slug'       => $group->slug . '_manage',
     265                        'screen_function'   => 'groups_screen_group_admin',
     266                        'position'          => 20,
     267                        'user_has_access'   => bp_is_item_admin(),
     268                        'show_in_admin_bar' => true,
     269                ) );
     270        }
     271
     272        /**
     273         * Register the Attachment post type
     274         *
     275         * @since BuddyPress (2.2.0)
     276         */
     277        public function register_post_types() {
     278
     279                $bp_attachments_labels = array(
     280                        'name'               => __( 'BuddyPress Attachments',                                             'buddypress' ),
     281                        'singular'           => _x( 'Attachment',                    'bp-attachments singular',           'buddypress' ),
     282                        'menu_name'          => _x( 'Attachments',                   'bp-attachments menu name',          'buddypress' ),
     283                        'all_items'          => _x( 'All Attachments',               'bp-attachments all items',          'buddypress' ),
     284                        'singular_name'      => _x( 'Attachment',                    'bp-attachments singular name',      'buddypress' ),
     285                        'add_new'            => _x( 'Add New Attachment',            'bp-attachments add new',            'buddypress' ),
     286                        'edit_item'          => _x( 'Edit Attachment',               'bp-attachments edit item',          'buddypress' ),
     287                        'new_item'           => _x( 'New Attachment',                'bp-attachments new item',           'buddypress' ),
     288                        'view_item'          => _x( 'View Attachment',               'bp-attachments view item',          'buddypress' ),
     289                        'search_items'       => _x( 'Search Attachments',            'bp-attachments search items',       'buddypress' ),
     290                        'not_found'          => _x( 'No Attachments Found',          'bp-attachments not found',          'buddypress' ),
     291                        'not_found_in_trash' => _x( 'No Attachments Found in Trash', 'bp-attachments not found in trash', 'buddypress' )
     292                );
     293
     294                $bp_attachments_args = array(
     295                        'label'             => _x( 'Attachments',                    'bp-attachments label',              'buddypress' ),
     296                        'labels'            => $bp_attachments_labels,
     297                        'public'            => false,
     298                        'rewrite'           => false,
     299                        'show_ui'           => false, // As BP Attachments can be activated on the network, we need to use a specific UI
     300                        'show_in_admin_bar' => false,
     301                        'show_in_nav_menus' => false,
     302                        'capabilities'      => bp_attachments_get_attachment_caps(),
     303                        'capability_type'   => array( 'bp_attachment', 'bp_attachments' ),
     304                        'delete_with_user'  => true,
     305                        'supports'          => array( 'title', 'author' )
     306                );
     307
     308                // Register the post type for attachments.
     309                register_post_type( 'bp_attachment', $bp_attachments_args );
     310
     311                parent::register_post_types();
     312        }
     313
     314        /**
     315         * Register the Component taxonomy
     316         *
     317         * @since BuddyPress (2.2.0)
     318         */
     319        public function register_taxonomies() {
     320                $labels = array(
     321                        'name'              => _x( 'BuddyPress Components', 'bp-attachments taxonomy general name',  'buddypress' ),
     322                        'singular_name'     => _x( 'Component',             'bp-attachments taxonomy singular name', 'buddypress' ),
     323                        'search_items'      => _x( 'Search Components',     'bp-attachments search items',           'buddypress' ),
     324                        'all_items'         => _x( 'All Components',        'bp-attachments all items',              'buddypress' ),
     325                        'parent_item'       => _x( 'Parent Component',      'bp-attachments parent item',            'buddypress' ),
     326                        'parent_item_colon' => _x( 'Parent Component:',     'bp-attachments parent item colon',      'buddypress' ),
     327                        'edit_item'         => _x( 'Edit Component',        'bp-attachments edit item',              'buddypress' ),
     328                        'update_item'       => _x( 'Update Component',      'bp-attachments update item',            'buddypress' ),
     329                        'add_new_item'      => _x( 'Add New Component',     'bp-attachments add new item',           'buddypress' ),
     330                        'new_item_name'     => _x( 'New Component Name',    'bp-attachments new item name',          'buddypress' ),
     331                        'menu_name'         => _x( 'BP Component',          'bp-attachments menu name',              'buddypress' ),
     332                );
     333
     334                $args = array(
     335                        'hierarchical'          => true,
     336                        'labels'                => $labels,
     337                        'show_ui'               => true,
     338                        'show_admin_column'     => true,
     339                        'query_var'             => false,
     340                        'rewrite'               => false,
     341                        'capabilities'          => bp_attachments_get_component_caps(),
     342                        'update_count_callback' => '_update_generic_term_count',
     343                );
     344
     345                // Register the taxonomy for attachments.
     346                register_taxonomy( 'bp_component', array( 'bp_attachment' ), $args );
     347
     348                parent::register_taxonomies();
     349        }
     350
     351        /**
     352         * Map component ids with term ids if needed
     353         *
     354         * @since BuddyPress (2.2.0)
     355         */
     356        public function component_terms() {
     357                $bp = buddypress();
     358
     359                // Get terms mapping
     360                $terms_mapping = bp_get_option( 'bp_attachments_term_ids', array() );
     361                $terms_toadd = array();
     362
     363                // Loop in loaded components to check if the ones who need attachments are already mapped
     364                foreach ( array_keys( $bp->loaded_components ) as $component_id ) {
     365                        if ( ! empty( $bp->{$component_id}->can_attachments ) && empty( $terms_mapping[ $bp->{$component_id}->id ] ) ) {
     366                                $terms_toadd[ $bp->{$component_id}->id ] = array(
     367                                        'name' => $bp->{$component_id}->name,
     368                                        'slug' => $bp->{$component_id}->slug
     369                                );
     370                        }
     371                }
     372
     373                // No term to add, simply globalize the terms
     374                if ( empty( $terms_toadd ) ) {
     375                        $bp->{$this->id}->component_terms = $terms_mapping;
     376                        return;
     377                }
     378
     379                // Insert needed terms and build the mapping
     380                foreach ( $terms_toadd as $key => $component ) {
     381                        $component_term = wp_insert_term( $component['name'], 'bp_component', array( 'slug' => $component['slug'] ) );
     382
     383                        if ( ! empty( $component_term['term_id'] ) ) {
     384                                $terms_mapping[ $key ] = $component_term['term_id'];
     385                        }
     386                }
     387
     388                // Update terms mapping and globalize it
     389                bp_update_option( 'bp_attachments_term_ids', $terms_mapping );
     390                $bp->{$this->id}->component_terms = $terms_mapping;
     391        }
     392}
     393
     394/**
     395 * Bootstrap the Attachments component.
     396 */
     397function bp_setup_attachments() {
     398        buddypress()->attachments = new BP_Attachments_Component();
     399}
     400add_action( 'bp_setup_components', 'bp_setup_attachments', 6 );
  • src/bp-attachments/bp-attachments-screens.php

    diff --git src/bp-attachments/bp-attachments-screens.php src/bp-attachments/bp-attachments-screens.php
    index e69de29..3e98e8a 100644
     
     1<?php
     2/**
     3 * Attachments Screens.
     4 *
     5 * @package BuddyPress
     6 * @subpackage Attachments Screens
     7 */
     8
     9// Exit if accessed directly
     10defined( 'ABSPATH' ) or exit;
     11
     12/**
     13 * Handles the uploading and cropping of a user avatar. Displays the change avatar page.
     14 *
     15 * @since BuddyPress (2.2.0)
     16 *
     17 * @package BuddyPress
     18 * @subpackage Attachments Screens
     19 *
     20 * @uses bp_is_my_profile() Checks to make sure the current user being viewed equals the logged in user
     21 * @uses bp_core_load_template() Looks for and loads a template file within the current member theme (folder/filename)
     22 */
     23function bp_attachments_screen_change_avatar() {
     24        // Bail if not the correct screen
     25        if ( ! bp_is_my_profile() && ! bp_current_user_can( 'bp_moderate' ) ) {
     26                return false;
     27        }
     28
     29        // Bail if there are action variables
     30        if ( bp_action_variables() ) {
     31                bp_do_404();
     32                return;
     33        }
     34
     35        $bp = buddypress();
     36
     37        if ( ! isset( $bp->avatar_admin ) ) {
     38                $bp->avatar_admin = new stdClass();
     39        }
     40
     41        $bp->avatar_admin->step = 'upload-image';
     42        $user_id                = bp_displayed_user_id();
     43
     44        if ( ! empty( $_FILES ) ) {
     45
     46                // Check the nonce
     47                check_admin_referer( 'bp_avatar_upload' );
     48
     49                // Pass the file to the avatar upload handler
     50                $uploaded_avatar = bp_attachments_avatar_handle_upload( $_FILES, 'xprofile', $user_id );
     51
     52                if ( is_wp_error( $uploaded_avatar ) ) {
     53                        bp_core_add_message( $uploaded_avatar->get_error_message(), 'error' );
     54
     55                // Go to crop step
     56                } else {
     57                        // Avoid some error messages to be output if
     58                        // everything is ok.
     59                        $bp->template_message   = false;
     60                        $bp->avatar_admin->step = 'crop-image';
     61
     62                        // Make sure we include the jQuery jCrop file for image cropping
     63                        add_action( 'wp_print_scripts', 'bp_attachments_add_jquery_cropper' );
     64                }
     65        }
     66
     67        // If the image cropping is done, crop the image and save a full/thumb version
     68        if ( isset( $_POST['avatar-crop-submit'] ) ) {
     69
     70                // Check the nonce
     71                check_admin_referer( 'bp_avatar_cropstore' );
     72
     73                $args = array(
     74                        'item_id'       => $user_id,
     75                        'original_file' => $_POST['image_src'],
     76                        'crop_x'        => $_POST['x'],
     77                        'crop_y'        => $_POST['y'],
     78                        'crop_w'        => $_POST['w'],
     79                        'crop_h'        => $_POST['h']
     80                );
     81
     82                if ( ! bp_attachments_avatar_handle_crop( $args ) ) {
     83                        bp_core_add_message( __( 'There was a problem cropping your profile photo.', 'buddypress' ), 'error' );
     84                } else {
     85                        do_action( 'xprofile_avatar_uploaded' );
     86                        bp_core_add_message( __( 'Your new profile photo was uploaded successfully.', 'buddypress' ) );
     87                        bp_core_redirect( bp_displayed_user_domain() );
     88                }
     89        }
     90
     91        do_action( 'xprofile_screen_change_avatar' );
     92
     93        bp_core_load_template( apply_filters( 'xprofile_template_change_avatar', 'members/single/home' ) );
     94}
     95
     96/**
     97 * Handle the display of a group's Change Avatar page.
     98 *
     99 * @since BuddyPress (2.2.0)
     100 */
     101function bp_attachments_screen_group_admin_avatar() {
     102
     103        if ( 'group-avatar' != bp_get_group_current_admin_tab() ) {
     104                return false;
     105        }
     106
     107        // If the logged-in user doesn't have permission or if avatar uploads are disabled, then stop here
     108        if ( ! bp_is_item_admin() || ! bp_attachments_avatar_is_enabled() ) {
     109                return false;
     110        }
     111
     112        $bp = buddypress();
     113
     114        // If the group admin has deleted the admin avatar
     115        if ( bp_is_action_variable( 'delete', 1 ) ) {
     116
     117                // Check the nonce
     118                check_admin_referer( 'bp_group_avatar_delete' );
     119
     120                if ( bp_attachments_delete_existing_avatar( array( 'item_id' => $bp->groups->current_group->id, 'object' => 'group' ) ) ) {
     121                        bp_core_add_message( __( 'The group profile photo was deleted successfully!', 'buddypress' ) );
     122                } else {
     123                        bp_core_add_message( __( 'There was a problem deleting the group profile photo; please try again.', 'buddypress' ), 'error' );
     124                }
     125        }
     126
     127        if ( ! isset( $bp->avatar_admin ) ) {
     128                $bp->avatar_admin = new stdClass();
     129        }
     130
     131        $bp->avatar_admin->step = 'upload-image';
     132
     133        if ( !empty( $_FILES ) ) {
     134
     135                // Check the nonce
     136                check_admin_referer( 'bp_avatar_upload' );
     137
     138                // Pass the file to the avatar upload handler
     139                $uploaded_avatar = bp_attachments_avatar_handle_upload( $_FILES, 'groups', $bp->groups->current_group->id );
     140
     141                if ( is_wp_error( $uploaded_avatar ) ) {
     142                        bp_core_add_message( $uploaded_avatar->get_error_message(), 'error' );
     143
     144                // Go to crop step
     145                } else {
     146                        // Avoid some error messages to be output if
     147                        // everything is ok.
     148                        $bp->template_message   = false;
     149                        $bp->avatar_admin->step = 'crop-image';
     150
     151                        // Make sure we include the jQuery jCrop file for image cropping
     152                        add_action( 'wp_print_scripts', 'bp_attachments_add_jquery_cropper' );
     153                }
     154        }
     155
     156        // If the image cropping is done, crop the image and save a full/thumb version
     157        if ( isset( $_POST['avatar-crop-submit'] ) ) {
     158
     159                // Check the nonce
     160                check_admin_referer( 'bp_avatar_cropstore' );
     161
     162                $args = array(
     163                        'object'        => 'group',
     164                        'avatar_dir'    => 'avatar_group_dir',
     165                        'item_id'       => $bp->groups->current_group->id,
     166                        'original_file' => $_POST['image_src'],
     167                        'crop_x'        => $_POST['x'],
     168                        'crop_y'        => $_POST['y'],
     169                        'crop_w'        => $_POST['w'],
     170                        'crop_h'        => $_POST['h']
     171                );
     172
     173                if ( ! bp_attachments_avatar_handle_crop( $args ) ) {
     174                        bp_core_add_message( __( 'There was a problem cropping the group profile photo.', 'buddypress' ), 'error' );
     175                } else {
     176                        bp_core_add_message( __( 'The new group profile photo was uploaded successfully.', 'buddypress' ) );
     177                }
     178        }
     179
     180        do_action( 'groups_screen_group_admin_avatar', $bp->groups->current_group->id );
     181
     182        bp_core_load_template( apply_filters( 'groups_template_group_admin_avatar', 'groups/single/home' ) );
     183}
     184add_action( 'bp_screens', 'bp_attachments_screen_group_admin_avatar' );
     185
     186/**
     187 * Move the avatar in user's folder once the signup activated
     188 *
     189 * @since BuddyPress (2.2.0)
     190 */
     191function bp_attachments_avatar_activate_signup_screen( $hashed_key = '', $user = 0 ) {
     192        if ( empty( $hashed_key ) || empty( $user ) ) {
     193                return;
     194        }
     195
     196        // Check if the avatar folder exists. If it does, move rename it, move
     197        // it and delete the signup avatar dir
     198        if ( file_exists( bp_attachments_get_upload_dir( 'avatar_user_dir' ) . '/signups/' . $hashed_key ) ) {
     199                @rename( bp_attachments_get_upload_dir( 'avatar_user_dir' ) . '/signups/' . $hashed_key, bp_attachments_get_upload_dir( 'avatar_user_dir' ) . '/' . $user );
     200        }
     201}
     202add_action( 'bp_core_activate_account_avatar', 'bp_attachments_avatar_activate_signup_screen', 10, 2 );
  • src/bp-attachments/js/script.js

    diff --git src/bp-attachments/js/script.js src/bp-attachments/js/script.js
    index e69de29..35b9b5d 100644
     
     1var bp = bp || {};
     2
     3/**
     4 * BP Attachments Editor !
     5 */
     6( function( $ ) {
     7        var BPmedia;
     8
     9        bp.media = BPmedia = {};
     10
     11        // map bp.media to wp.media
     12        bp.media = wp.media;
     13
     14        bp.media.params = {
     15                item_id:        _wpPluploadSettings.defaults.multipart_params.item_id,
     16                item_component: _wpPluploadSettings.defaults.multipart_params.component,
     17                item_type:      _wpPluploadSettings.defaults.multipart_params.item_type,
     18                item_post_id:   _wpPluploadSettings.defaults.multipart_params.post_id,
     19                nonce:          _wpPluploadSettings.defaults.multipart_params._bpnonce,
     20                callback:       _wpPluploadSettings.defaults.multipart_params.callback,
     21                callback_id:    _wpPluploadSettings.defaults.multipart_params.callback_id,
     22        };
     23
     24        _.extend( BPmedia, { model: {}, view: {}, controller: {}, frames: {} } );
     25
     26        // Models
     27        bp.media.attachment = function( id ) {
     28                return BPattachment.get( id );
     29        };
     30
     31        /**
     32         * bp.media.model.Attachment
     33         *
     34         * It's an adpated copy of WordPress Attachment model
     35         *
     36         * @constructor
     37         * @augments Backbone.Model
     38         */
     39        BPattachment = BPmedia.model.Attachment = bp.media.model.Attachment.extend({
     40
     41                /**
     42                 * Triggered when attachment details change
     43                 * Overrides Backbone.Model.sync
     44                 *
     45                 * @param {string} method
     46                 * @param {bp.media.model.Attachment} model
     47                 * @param {Object} [options={}]
     48                 *
     49                 * @returns {Promise}
     50                 */
     51                sync: function( method, model, options ) {
     52                        // If the attachment does not yet have an `id`, return an instantly
     53                        // rejected promise. Otherwise, all of our requests will fail.
     54                        if ( _.isUndefined( this.id ) ) {
     55                                return $.Deferred().rejectWith( this ).promise();
     56                        }
     57
     58                        // Overload the `read` request so Attachment.fetch() functions correctly.
     59                        if ( 'read' === method ) {
     60                                options = options || {};
     61                                options.context = this;
     62                                options.data = _.extend( options.data || {}, {
     63                                        action: 'get_bp_attachment',
     64                                        id: this.id
     65                                });
     66                                return bp.media.ajax( options );
     67
     68                        // Overload the `update` request so properties can be saved.
     69                        } else if ( 'update' === method ) {
     70                                // If we do not have the necessary nonce, fail immeditately.
     71                                if ( ! this.get('nonces') || ! this.get('nonces').update ) {
     72                                        return $.Deferred().rejectWith( this ).promise();
     73                                }
     74
     75                                options = options || {};
     76                                options.context = this;
     77
     78                                // Set the action and ID.
     79                                options.data = _.extend( options.data || {}, {
     80                                        action:  'update_bp_attachment',
     81                                        id:      this.id,
     82                                        nonce:   this.get('nonces').update,
     83                                        post_id: bp.media.model.settings.post.id
     84                                });
     85
     86                                // Record the values of the changed attributes.
     87                                if ( model.hasChanged() ) {
     88                                        options.data.changes = {};
     89
     90                                        _.each( model.changed, function( value, key ) {
     91                                                options.data.changes[ key ] = this.get( key );
     92                                        }, this );
     93                                }
     94
     95                                return bp.media.ajax( options );
     96
     97                        // Overload the `delete` request so attachments can be removed.
     98                        // This will permanently delete an attachment.
     99                        } else if ( 'delete' === method ) {
     100                                options = options || {};
     101
     102                                if ( ! options.wait ) {
     103                                        this.destroyed = true;
     104                                }
     105
     106                                options.context = this;
     107                                options.data = _.extend( options.data || {}, {
     108                                        action:   'delete_bp_attachment',
     109                                        id:       this.id,
     110                                        _wpnonce: this.get('nonces')['delete']
     111                                });
     112
     113                                return bp.media.ajax( options ).done( function() {
     114                                        this.destroyed = true;
     115                                }).fail( function() {
     116                                        this.destroyed = false;
     117                                });
     118
     119                        // Otherwise, fall back to `Backbone.sync()`.
     120                        } else {
     121                                /**
     122                                 * Call `sync` directly on Backbone.Model
     123                                 */
     124                                return bp.media.model.Attachment.prototype.sync.apply( this, arguments );
     125                        }
     126                },
     127                /**
     128                 * Convert date strings into Date objects.
     129                 *
     130                 * @param {Object} resp The raw response object, typically returned by fetch()
     131                 * @returns {Object} The modified response object, which is the attributes hash
     132                 *    to be set on the model.
     133                 */
     134                parse: function( resp ) {
     135                        if ( ! resp ) {
     136                                return resp;
     137                        }
     138
     139                        resp.date = new Date( resp.date );
     140                        resp.modified = new Date( resp.modified );
     141                        return resp;
     142                },
     143        }, {
     144                /**
     145                 * Add a model to the end of the static 'all' collection and return it.
     146                 *
     147                 * @static
     148                 * @param {Object} attrs
     149                 * @returns {wp.media.model.Attachment}
     150                 */
     151                create: function( attrs ) {
     152                        return BPattachments.all.push( attrs );
     153                },
     154                /**
     155                 * Retrieve a model, or add it to the end of the static 'all' collection before returning it.
     156                 *
     157                 * @static
     158                 * @param {string} id A string used to identify a model.
     159                 * @param {Backbone.Model|undefined} attachment
     160                 * @returns {wp.media.model.Attachment}
     161                 */
     162                get: _.memoize( function( id, attachment ) {
     163                        return BPattachments.all.push( attachment || { id: id } );
     164                })
     165        });
     166
     167        /**
     168         * Override media.model.Attachment.create function
     169         * so that the uploader is using BPattachment model
     170         */
     171        bp.media.model.Attachment.create = function( attrs ) {
     172                return BPattachments.all.push( attrs );
     173        };
     174
     175        /**
     176         * Override media.model.Attachment.get function
     177         * so that the uploader is using BPattachment model
     178         */
     179        bp.media.model.Attachment.get = _.memoize( function( id, attachment ) {
     180                return BPattachments.all.push( attachment || { id: id } );
     181        } );
     182
     183        /**
     184         * bp.media.model.BPattachments
     185         *
     186         * it's an adapted copy of WordPress Attachments Collection
     187         *
     188         * @constructor
     189         * @augments Backbone.Collection
     190         */
     191        BPattachments = bp.media.model.Attachments.extend({
     192                /**
     193                 * @type {wp.media.model.Attachment}
     194                 */
     195                model: BPattachment,
     196                /**
     197                 * @param {Array} [models=[]] Array of models used to populate the collection.
     198                 * @param {Object} [options={}]
     199                 */
     200                initialize: function( models, options ) {
     201                        options = options || {};
     202
     203                        bp.media.model.Attachments.prototype.initialize.apply( this, arguments );
     204                },
     205
     206                /**
     207                 * @access private
     208                 */
     209                _requery: function() {
     210                        if ( this.props.get('query') ) {
     211                                this.mirror( bp.media.model.Query.get( this.props.toJSON() ) );
     212                        }
     213                },
     214
     215                parse: function( resp, xhr ) {
     216                        if ( ! _.isArray( resp ) ) {
     217                                resp = [resp];
     218                        }
     219
     220                        return _.map( resp, function( attrs ) {
     221                                var id, attachment, newAttributes;
     222
     223                                if ( attrs instanceof Backbone.Model ) {
     224                                        id = attrs.get( 'id' );
     225                                        attrs = attrs.attributes;
     226                                } else {
     227                                        id = attrs.id;
     228                                }
     229
     230                                attachment = BPattachment.get( id );
     231                                newAttributes = attachment.parse( attrs, xhr );
     232
     233                                if ( ! _.isEqual( attachment.attributes, newAttributes ) ) {
     234                                        attachment.set( newAttributes );
     235                                }
     236
     237                                return attachment;
     238                        });
     239                },
     240
     241        });
     242
     243        /**
     244         * @static
     245         * @member {bp.media.model.BPattachments}
     246         */
     247        BPattachments.all = new BPattachments();
     248
     249        /**
     250         * bp.media.model.Avatar
     251         *
     252         * @constructor
     253         * @augments Backbone.Model
     254         */
     255        Avatar = bp.media.model.Avatar = Backbone.Model.extend( {
     256                defaults : {
     257            id: 0,
     258                        title: '',
     259                        component: '',
     260                        filename: '',
     261                        url:'',
     262                        path:'',
     263        },
     264        } );
     265
     266        /**
     267         * bp.media.model.Query
     268         *
     269         * It's an adpated copy of WordPress Query
     270         *
     271         * @constructor
     272         * @augments Backbone.Model
     273         */
     274        BPquery = BPmedia.model.Query = bp.media.model.Query = BPattachments.extend({
     275
     276                initialize: function( models, options ) {
     277                        var allowed;
     278
     279                        options = options || {};
     280                        BPattachments.prototype.initialize.apply( this, arguments );
     281
     282                        this.args     = options.args;
     283                        this._hasMore = true;
     284                        this.created  = new Date();
     285
     286                        this.filters.order = function( attachment ) {
     287                                var orderby = this.props.get('orderby'),
     288                                        order = this.props.get('order');
     289
     290                                if ( ! this.comparator ) {
     291                                        return true;
     292                                }
     293
     294                                // We want any items that can be placed before the last
     295                                // item in the set. If we add any items after the last
     296                                // item, then we can't guarantee the set is complete.
     297                                if ( this.length ) {
     298                                        return 1 !== this.comparator( attachment, this.last(), { ties: true });
     299
     300                                // Handle the case where there are no items yet and
     301                                // we're sorting for recent items. In that case, we want
     302                                // changes that occurred after we created the query.
     303                                } else if ( 'DESC' === order && ( 'date' === orderby || 'modified' === orderby ) ) {
     304                                        return attachment.get( orderby ) >= this.created;
     305
     306                                // If we're sorting by menu order and we have no items,
     307                                // accept any items that have the default menu order (0).
     308                                } else if ( 'ASC' === order && 'menuOrder' === orderby ) {
     309                                        return attachment.get( orderby ) === 0;
     310                                }
     311
     312                                // Otherwise, we don't want any items yet.
     313                                return false;
     314                        };
     315
     316                        // Observe the central `wp.Uploader.queue` collection to watch for
     317                        // new matches for the query.
     318                        //
     319                        // Only observe when a limited number of query args are set. There
     320                        // are no filters for other properties, so observing will result in
     321                        // false positives in those queries.
     322                        allowed = [ 's', 'order', 'orderby', 'posts_per_page', 'post_mime_type', 'post_parent', 'component', 'item_type', 'item_id' ];
     323                        if ( wp.Uploader && _( this.args ).chain().keys().difference( allowed ).isEmpty().value() ) {
     324                                this.observe( wp.Uploader.queue );
     325                        }
     326                },
     327                /**
     328                 * @returns {Boolean}
     329                 */
     330                hasMore: function() {
     331                        return this._hasMore;
     332                },
     333                /**
     334                 * @param {Object} [options={}]
     335                 * @returns {Promise}
     336                 */
     337                more: function( options ) {
     338                        var query = this;
     339
     340                        if ( this._more && 'pending' === this._more.state() ) {
     341                                return this._more;
     342                        }
     343
     344                        if ( ! this.hasMore() || 'avatar' == this.args.item_type ) {
     345                                return $.Deferred().resolveWith( this ).promise();
     346                        }
     347
     348                        options = options || {};
     349                        options.remove = false;
     350
     351                        return this._more = this.fetch( options ).done( function( resp ) {
     352                                if ( _.isEmpty( resp ) || -1 === this.args.posts_per_page || resp.length < this.args.posts_per_page ) {
     353                                        query._hasMore = false;
     354                                }
     355                        });
     356                },
     357
     358                sync: function( method, model, options ) {
     359                        var args, fallback;
     360
     361                        // Overload the read method so BPattachment.fetch() functions correctly.
     362                        if ( 'read' === method ) {
     363                                options = options || {};
     364                                options.context = this;
     365
     366                                if ( 'avatar' == this.args.item_type ) {
     367                                        return false;
     368                                }
     369
     370                                options.data = _.extend( options.data || {}, {
     371                                        action:  'query_bp_attachments',
     372                                        post_id: bp.media.model.settings.post.id
     373                                });
     374
     375                                // Clone the args so manipulation is non-destructive.
     376                                args = _.clone( this.args );
     377
     378                                // Determine which page to query.
     379                                if ( -1 !== args.posts_per_page ) {
     380                                        args.paged = Math.floor( this.length / args.posts_per_page ) + 1;
     381                                }
     382
     383                                options.data.query = args;
     384                                return bp.media.ajax( options );
     385
     386                        // Otherwise, fall back to Backbone.sync()
     387                        } else {
     388                                /**
     389                                 * Call bp.media.model.BPattachments.sync or Backbone.sync
     390                                 */
     391                                fallback = BPattachments.prototype.sync ? BPattachments.prototype : Backbone;
     392                                return fallback.sync.apply( this, arguments );
     393                        }
     394                }
     395
     396        }, {
     397                /**
     398                 * @readonly
     399                 */
     400                defaultProps: {
     401                        orderby: 'date',
     402                        order:   'DESC',
     403                        component: bp.media.params.item_component,
     404                        item_type: bp.media.params.item_type,
     405                        item_id: bp.media.params.item_id,
     406                },
     407                /**
     408                 * @readonly
     409                 */
     410                defaultArgs: {
     411                        posts_per_page: 40
     412                },
     413                /**
     414                 * @readonly
     415                 */
     416                orderby: {
     417                        allowed:  [ 'name', 'author', 'date', 'title', 'modified', 'uploadedTo', 'id', 'post__in', 'menuOrder' ],
     418                        valuemap: {
     419                                'id':         'ID',
     420                                'uploadedTo': 'parent',
     421                                'attachedTo': 'item_id',
     422                                'menuOrder':  'menu_order ID'
     423                        }
     424                },
     425                /**
     426                 * @readonly
     427                 */
     428                propmap: {
     429                        'search':    's',
     430                        'type':      'post_mime_type',
     431                        'perPage':   'posts_per_page',
     432                        'menuOrder': 'menu_order',
     433                        'uploadedTo': 'post_parent',
     434                        'attachedTo': 'item_id',
     435                },
     436                /**
     437                 * @static
     438                 * @method
     439                 *
     440                 * @returns {wp.media.model.Query} A new query.
     441                 */
     442                // Caches query objects so queries can be easily reused.
     443                get: (function(){
     444                        /**
     445                         * @static
     446                         * @type Array
     447                         */
     448                        var queries = [];
     449
     450                        /**
     451                         * @param {Object} props
     452                         * @param {Object} options
     453                         * @returns {Query}
     454                         */
     455                        return function( props, options ) {
     456                                var args     = {},
     457                                        orderby  = BPquery.orderby,
     458                                        defaults = BPquery.defaultProps,
     459                                        query;
     460
     461                                // Remove the `query` property. This isn't linked to a query,
     462                                // this *is* the query.
     463                                delete props.query;
     464
     465                                // Fill default args.
     466                                _.defaults( props, defaults );
     467
     468                                // Normalize the order.
     469                                props.order = props.order.toUpperCase();
     470                                if ( 'DESC' !== props.order && 'ASC' !== props.order ) {
     471                                        props.order = defaults.order.toUpperCase();
     472                                }
     473
     474                                // Ensure we have a valid orderby value.
     475                                if ( ! _.contains( orderby.allowed, props.orderby ) ) {
     476                                        props.orderby = defaults.orderby;
     477                                }
     478
     479                                // Generate the query `args` object.
     480                                // Correct any differing property names.
     481                                _.each( props, function( value, prop ) {
     482                                        if ( _.isNull( value ) ) {
     483                                                return;
     484                                        }
     485
     486                                        args[ BPquery.propmap[ prop ] || prop ] = value;
     487                                });
     488
     489                                // Fill any other default query args.
     490                                _.defaults( args, BPquery.defaultArgs );
     491
     492                                // `props.orderby` does not always map directly to `args.orderby`.
     493                                // Substitute exceptions specified in orderby.keymap.
     494                                args.orderby = orderby.valuemap[ props.orderby ] || props.orderby;
     495
     496                                // Search the query cache for matches.
     497                                query = _.find( queries, function( query ) {
     498                                        return _.isEqual( query.args, args );
     499                                });
     500
     501                                // Otherwise, create a new query and add it to the cache.
     502                                if ( ! query ) {
     503                                        query = new BPquery( [], _.extend( options || {}, {
     504                                                props: props,
     505                                                args:  args
     506                                        } ) );
     507                                        queries.push( query );
     508                                }
     509
     510                                return query;
     511                        };
     512                }())
     513
     514        });
     515
     516        bp.media.query = function( props ) {
     517                return new BPattachments( null, {
     518                        props: _.extend( _.defaults( props || {}, { orderby: 'date' } ), { query: true } )
     519                });
     520        };
     521
     522        bp.media.controller.bpLibrary = bp.media.controller.Library.extend({
     523                defaults: {
     524                        id:         'bp_library',
     525                        multiple:   false,
     526                        describe:   false,
     527                        toolbar:    'bp_select',
     528                        sidebar:    'bp_settings',
     529                        content:    'bp_upload',
     530                        router:     'bp_browse',
     531                        menu:       'default',
     532                        searchable: false,
     533                        filterable: false,
     534                        sortable:   false,
     535                        title:      bp.media.view.l10n.bp_attachments.title,
     536
     537                        // Uses a user setting to override the content mode.
     538                        contentUserSetting: false,
     539
     540                        // Sync the selection from the last state when 'multiple' matches.
     541                        syncSelection: true
     542                },
     543
     544                initialize: function() {
     545                        type = '';
     546
     547                        if ( 'avatar' == bp.media.params.item_type ) {
     548                                type = { type: 'image' };
     549                        }
     550
     551                        if ( ! this.get('library') ) {
     552                                this.set( 'library', bp.media.query( type ) );
     553                        }
     554
     555                        bp.media.controller.Library.prototype.initialize.apply( this, arguments );
     556                },
     557
     558                uploading: function( attachment ) {
     559                        var content = this.frame.content,
     560                                mode = 'bpbrowse';
     561
     562                        if ( 'avatar' == bp.media.params.item_type )
     563                                mode = 'crop';
     564
     565                        if ( 'bp_upload' === content.mode() ) {
     566                                this.frame.content.mode( mode );
     567                        }
     568
     569                        this.get('selection').add( attachment );
     570                },
     571
     572        });
     573
     574        BPmedia.view.Avatar = bp.media.view.Avatar = bp.media.view.Attachment.extend( {
     575                tagName:   'li',
     576                className: 'avatar',
     577                template:  bp.media.template('avatar'),
     578
     579                initialize: function() {
     580                        var selection = this.options.selection;
     581
     582                        bp.media.view.Attachment.prototype.initialize.apply( this, arguments );
     583                },
     584
     585                render: function() {
     586                        var selection = this.options.selection;
     587
     588                        bp.media.view.Attachment.prototype.render.apply( this, arguments );
     589
     590                        if( ! _.isUndefined( selection.single() ) && ! selection.single()._changing ) {
     591                                var tocrop = this.$el.find( 'img' );
     592                                var ratio = 1;
     593
     594                                if ( ! _.isUndefined( bp.media.view.settings.bp_attachments.full_h ) && ! _.isUndefined( bp.media.view.settings.bp_attachments.full_w ) && bp.media.view.settings.bp_attachments.full_h != bp.media.view.settings.bp_attachments.full_w ) {
     595                                        ratio = bp.media.view.settings.bp_attachments.full_h / bp.media.view.settings.bp_attachments.full_w;
     596                                }
     597
     598                                crop_left   = Math.round( selection.single().get('width' ) / 4 );
     599                                crop_top    = Math.round( selection.single().get('height' ) / 4 );
     600                                crop_right  = selection.single().get('width' ) - crop_left;
     601                                crop_bottom = selection.single().get('height' ) - crop_top;
     602
     603                                tocrop.Jcrop({
     604                                        onChange: this.showPreview,
     605                                        onSelect: this.showPreview,
     606                                        onSelect: this.updateCoords,
     607                                        aspectRatio: ratio,
     608                                setSelect: [ crop_left, crop_top, crop_right, crop_bottom ]
     609                                });
     610
     611                                this.updateCoords({x: crop_left, y: crop_top, w: crop_right, h: crop_bottom});
     612
     613                        }
     614
     615                        return this;
     616                },
     617
     618                updateCoords: function( args ) {
     619                        var selection = BPmedia.frame().state().get( 'selection' ).single();
     620
     621                        // Need to find a better way
     622                        selection.attributes.x = args.x;
     623                        selection.attributes.y = args.y;
     624                        selection.attributes.w = args.w;
     625                        selection.attributes.h = args.h;
     626                },
     627
     628                showPreview: function( coords ) {
     629                        var selection = BPmedia.frame().state().get( 'selection' ).single();
     630
     631                        // For some reason, the Avatar Detail view is not rendered
     632                        $( '#avatar-crop-preview' ).prop( 'src', selection.get( 'url') );
     633
     634                        if ( parseInt(coords.w) > 0 ) {
     635                                var fw = bp.media.view.settings.bp_attachments.full_w;
     636                                var fh = bp.media.view.settings.bp_attachments.full_h;
     637                                var rx = fw / coords.w;
     638                                var ry = fh / coords.h;
     639
     640                                $( '#avatar-crop-preview' ).css( {
     641                                        maxWidth:'none',
     642                                        width: Math.round(rx *  selection.get('width') )+ 'px',
     643                                        height: Math.round(ry * selection.get('height') )+ 'px',
     644                                        marginLeft: '-' + Math.round(rx * coords.x) + 'px',
     645                                        marginTop: '-' + Math.round(ry * coords.y) + 'px'
     646                                } );
     647                        }
     648                },
     649        } );
     650
     651        bp.media.view.AvatarDetails = bp.media.view.Attachment.Details.extend({
     652
     653                tagName:   'div',
     654                className: 'attachment-details',
     655                template:  bp.media.template('bp-avatar-details'),
     656
     657                render: function() {
     658                        var options = _.defaults( this.model.toJSON(), {
     659                                        orientation:   'landscape',
     660                                        uploading:     false,
     661                                        type:          '',
     662                                        subtype:       '',
     663                                        icon:          '',
     664                                        filename:      '',
     665                                        caption:       '',
     666                                        title:         '',
     667                                        dateFormatted: '',
     668                                        width:         '',
     669                                        height:        '',
     670                                        compat:        false,
     671                                        alt:           '',
     672                                        description:   ''
     673                                });
     674
     675                        options.buttons  = this.buttons;
     676                        options.describe = this.controller.state().get('describe');
     677
     678                        if ( 'image' === options.type ) {
     679                                options.size = this.imageSize();
     680                        }
     681
     682                        options.can = {};
     683                        if ( options.nonces ) {
     684                                options.can.remove = !! options.nonces['delete'];
     685                                options.can.save = !! options.nonces.update;
     686                        }
     687
     688                        if ( this.controller.state().get('allowLocalEdits') ) {
     689                                options.allowLocalEdits = true;
     690                        }
     691
     692                        this.views.detach();
     693                        this.$el.html( this.template( options ) );
     694
     695                        this.$el.toggleClass( 'uploading', options.uploading );
     696                        if ( options.uploading ) {
     697                                this.$bar = this.$('.media-progress-bar div');
     698                        } else {
     699                                delete this.$bar;
     700                        }
     701
     702                        // Check if the model is selected.
     703                        this.updateSelect();
     704
     705                        // Update the save status.
     706                        this.updateSave();
     707
     708                        /**
     709                         *
     710                         * ! this is not rendered ?
     711                         *
     712                         *
     713                         */
     714                        this.views.render();
     715
     716                        return this;
     717                },
     718        });
     719
     720        bp.media.view.AttachmentsDetails = bp.media.view.Attachment.Details.extend({
     721
     722                tagName:   'div',
     723                className: 'attachment-details',
     724                template:  bp.media.template('bp-attachment-details'),
     725
     726                render: function() {
     727                        var options = _.defaults( this.model.toJSON(), {
     728                                        orientation:   'landscape',
     729                                        uploading:     false,
     730                                        type:          '',
     731                                        subtype:       '',
     732                                        icon:          '',
     733                                        filename:      '',
     734                                        caption:       '',
     735                                        title:         '',
     736                                        dateFormatted: '',
     737                                        width:         '',
     738                                        height:        '',
     739                                        compat:        false,
     740                                        alt:           '',
     741                                        description:   ''
     742                                });
     743
     744                        options.buttons  = this.buttons;
     745                        options.describe = this.controller.state().get('describe');
     746
     747                        if ( 'image' === options.type ) {
     748                                options.size = this.imageSize();
     749                        }
     750
     751                        options.can = {};
     752                        if ( options.nonces ) {
     753                                options.can.remove = !! options.nonces['delete'];
     754                                options.can.save = !! options.nonces.update;
     755                        }
     756
     757                        if ( this.controller.state().get('allowLocalEdits') ) {
     758                                options.allowLocalEdits = true;
     759                        }
     760
     761                        this.views.detach();
     762                        this.$el.html( this.template( options ) );
     763
     764                        this.$el.toggleClass( 'uploading', options.uploading );
     765                        if ( options.uploading ) {
     766                                this.$bar = this.$('.media-progress-bar div');
     767                        } else {
     768                                delete this.$bar;
     769                        }
     770
     771                        // Check if the model is selected.
     772                        this.updateSelect();
     773
     774                        // Update the save status.
     775                        this.updateSave();
     776
     777                        this.views.render();
     778
     779                        return this;
     780                },
     781
     782        });
     783
     784        BPmedia.view.AttachmentsBrowser = bp.media.view.BPattachmentsBrowser = bp.media.view.AttachmentsBrowser.extend({
     785
     786                createToolbar: function () {
     787                        bp.media.view.AttachmentsBrowser.prototype.createToolbar.apply( this, arguments );
     788
     789                        // Remove the date field and label
     790                        this.toolbar.unset( 'dateFilterLabel' );
     791                        this.toolbar.unset( 'dateFilter' );
     792                },
     793
     794                createSingle: function() {
     795                        var sidebar = this.sidebar,
     796                                single = this.options.selection.single();
     797
     798                        sidebar.set( 'details', new bp.media.view.AttachmentsDetails({
     799                                controller: this.controller,
     800                                model:      single,
     801                                priority:   80
     802                        }) );
     803
     804                        if ( this.options.display ) {
     805                                sidebar.set( 'display', new bp.media.view.Settings.AttachmentDisplay({
     806                                        controller:   this.controller,
     807                                        model:        this.model.display( single ),
     808                                        attachment:   single,
     809                                        priority:     160,
     810                                        userSettings: this.model.get('displayUserSettings')
     811                                }) );
     812                        }
     813                },
     814        });
     815
     816        BPmedia.view.AvatarCroper = bp.media.view.AvatarCroper = bp.media.view.AttachmentsBrowser.extend( {
     817                tagName:   'div',
     818                className: 'attachments-browser',
     819
     820                initialize: function() {
     821                        _.defaults( this.options, {
     822                                filters: false,
     823                                search:  false,
     824                                display: false,
     825
     826                                AttachmentView: bp.media.view.Avatar
     827                        });
     828
     829                        this.createToolbar();
     830                        this.updateContent();
     831                        this.createSidebar();
     832
     833                        this.collection.on( 'add remove reset', this.updateContent, this );
     834                        this.on( 'ready', this.customizeUploader, this );
     835                },
     836
     837                customizeUploader:function(){
     838                        $( '.upload-ui h3.upload-instructions' ).html( 'Drop your avatar anywhere to upload' );
     839                        $( '.upload-ui a.button-hero' ).html( 'Select an image' );
     840                },
     841
     842                createToolbar: function () {
     843                        bp.media.view.AttachmentsBrowser.prototype.createToolbar.apply( this, arguments );
     844
     845                        // Remove the date field and label
     846                        this.toolbar.unset( 'dateFilterLabel' );
     847                        this.toolbar.unset( 'dateFilter' );
     848                },
     849
     850                updateContent: function() {
     851                        var view = this;
     852
     853                        if( ! this.attachments ) {
     854                                this.createAttachments();
     855                        }
     856
     857                        if ( ! this.collection.length ) {
     858                                this.collection.more().done( function() {
     859                                        if ( ! view.collection.length ) {
     860                                                view.createUploader();
     861                                        }
     862                                });
     863                        }
     864                },
     865
     866                createAttachments: function() {
     867                        this.removeContent();
     868
     869                        this.attachments = new bp.media.view.Attachments({
     870                                controller: this.controller,
     871                                collection: this.collection,
     872                                selection:  this.options.selection,
     873                                model:      this.model,
     874                                sortable:   this.options.sortable,
     875
     876                                // The single `Attachment` view to be used in the `Attachments` view.
     877                                AttachmentView: this.options.AttachmentView
     878                        });
     879
     880                        this.views.add( this.attachments );
     881                },
     882
     883                removeContent: function() {
     884                        _.each(['attachments','uploader'], function( key ) {
     885                                if ( this[ key ] ) {
     886                                        this[ key ].remove();
     887                                        delete this[ key ];
     888                                }
     889                        }, this );
     890                },
     891
     892                createSidebar: function() {
     893                        var options = this.options,
     894                                selection = options.selection,
     895                                sidebar = this.sidebar = new bp.media.view.Sidebar({
     896                                        controller: this.controller
     897                                });
     898
     899                        this.views.add( sidebar );
     900
     901                        if ( this.controller.uploader ) {
     902                                sidebar.set( 'uploads', new bp.media.view.UploaderStatus({
     903                                        controller: this.controller,
     904                                        priority:   40
     905                                }) );
     906                        }
     907
     908                        selection.on( 'selection:single', this.createSingle, this );
     909                        selection.on( 'selection:unsingle', this.disposeSingle, this );
     910
     911                        if ( selection.single() ) {
     912                                this.createSingle();
     913                        }
     914                },
     915
     916                createSingle: function() {
     917                        var sidebar = this.sidebar,
     918                                single = this.options.selection.single();
     919
     920                        sidebar.set( 'details', new bp.media.view.AvatarDetails({
     921                                controller: this.controller,
     922                                model:      single,
     923                                priority:   80
     924                        }) );
     925                },
     926        } );
     927
     928        bp.media.UploaderInline = bp.media.view.UploaderInline.extend( {
     929                initialize:function() {
     930                        bp.media.view.UploaderInline.prototype.initialize.apply( this, arguments );
     931
     932                        if ( 'avatar' == bp.media.params.item_type ) {
     933                                this.on( 'ready', this.customizeUploader, this );
     934                        }
     935                },
     936
     937                customizeUploader:function(){
     938                        $( '.upload-ui h3.upload-instructions' ).html( 'Drop your avatar anywhere to upload' );
     939                        $( '.upload-ui a.button-hero' ).html( 'Select an image' );
     940                }
     941        } );
     942
     943        bp.media.view.ToolbarSelect = bp.media.view.Toolbar.Select.extend({
     944                initialize: function() {
     945                        var options = this.options;
     946                        /**
     947                         * call 'initialize' directly on the parent class
     948                         */
     949                        bp.media.view.Toolbar.Select.prototype.initialize.apply( this, arguments );
     950                },
     951
     952                refresh: function() {
     953                        var library = BPmedia.BPattachmentsBrowser._frame.state().get( 'library' ),
     954                            selection = BPmedia.BPattachmentsBrowser._frame.state().get('selection');
     955
     956                        if ( -1 == bp.media.params.callback.indexOf( 'http://' ) ) {
     957                                if ( selection.length > 0 ) {
     958                                        this.get( 'select' ).model.set( 'disabled', false );
     959                                } else {
     960                                        this.get( 'select' ).model.set( 'disabled', true );
     961                                }
     962                        } else {
     963                                this.get( 'select' ).model.set( 'text', 'Ok' );
     964                                if ( library.length > 0 ) {
     965                                        this.get( 'select' ).model.set( 'disabled', false );
     966                                } else {
     967                                        this.get( 'select' ).model.set( 'disabled', true );
     968                                }
     969                        }
     970                },
     971        });
     972
     973        BPmedia.BPattachmentsBrowser = _.extend( BPmedia, {
     974                frame: function() {
     975                        if ( this._frame )
     976                                return this._frame;
     977
     978                        var states = [new bp.media.controller.bpLibrary()];
     979
     980                        this._frame = bp.media( {
     981                                className: 'media-frame no-sidebar',
     982                                states: states,
     983                                state: 'bp_library'
     984                        } );
     985
     986                        this._frame.on( 'open', this.open );
     987                        this._frame.on( 'close', this.close );
     988                        this._frame.on( 'router:create:bp_browse', this.createRouter, this  );
     989                        this._frame.on( 'router:render:bp_browse', this.bpBrowse, this );
     990                        this._frame.on( 'content:create:bpbrowse', this.bpBrowseContent, this );
     991                        this._frame.on( 'content:create:crop', this.cropAvatar, this );
     992                        this._frame.on( 'content:render:bp_upload', this.uploadContent, this );
     993                        this._frame.on( 'toolbar:create:bp_select', this.createSelectToolbar, this );
     994
     995                        this._frame.state( 'bp_library' ).on( 'select', this.select );
     996
     997                        // Check if one file at a time
     998                        this._frame.listenToOnce( this._frame.states.get('bp_library').frame.uploader, 'ready', this.oneAtatime, this );
     999
     1000                        return this._frame;
     1001                },
     1002
     1003                oneAtatime:function() {
     1004                        // plupload customs 1 at a time if set so !
     1005                        this.uploader.uploader.uploader.bind( 'FilesAdded', function( up, files ) {
     1006                                // one file at a time !
     1007                                if( _wpPluploadSettings.defaults.multi_selection == false && files.length > 1 ) {
     1008                                        var default_error = pluploadL10n.default_error;
     1009                                        pluploadL10n.default_error = bp.media.view.l10n.bp_attachments.files_error;
     1010
     1011                                        for ( i in files ) {
     1012                                                this.trigger( 'Error', {
     1013                                                code : 'bp_failed',
     1014                                                    file : files[i]
     1015                                        });
     1016                                                up.removeFile(files[i]);
     1017                                        }
     1018                                        pluploadL10n.default_error = default_error;
     1019                                }
     1020                        } );
     1021                },
     1022
     1023                createRouter:function( router ) {
     1024                        router.view = new bp.media.view.Router({
     1025                                controller: this._frame
     1026                        });
     1027                },
     1028
     1029                bpBrowse:function( view ) {
     1030
     1031                        if ( 'avatar' == bp.media.params.item_type ) {
     1032                                view.set({
     1033                                        bp_upload: {
     1034                                                text:     bp.media.view.l10n.bp_attachments.uploadtab,
     1035                                                priority: 20
     1036                                        },
     1037                                        crop: {
     1038                                                text:     bp.media.view.l10n.bp_attachments.croptab,
     1039                                                priority: 40
     1040                                        }
     1041                                });
     1042                        } else {
     1043                                view.set({
     1044                                        bp_upload: {
     1045                                                text:     bp.media.view.l10n.bp_attachments.uploadtab,
     1046                                                priority: 20
     1047                                        },
     1048                                        bpbrowse: {
     1049                                                text:     bp.media.view.l10n.bp_attachments.managetab,
     1050                                                priority: 40
     1051                                        }
     1052                                });
     1053                        }
     1054
     1055                },
     1056
     1057                bpBrowseContent:function( content ) {
     1058                        var state = this._frame.state();
     1059
     1060                        this._frame.$el.removeClass('hide-toolbar');
     1061
     1062                        // Browse our library of attachments.
     1063                        content.view = new BPmedia.view.AttachmentsBrowser({
     1064                                controller: this._frame,
     1065                                collection: state.get('library'),
     1066                                selection:  state.get('selection'),
     1067                                model:      state,
     1068                                sortable:   state.get('sortable'),
     1069                                search:     state.get('searchable'),
     1070                                filters:    state.get('filterable'),
     1071                                display:    state.get('displaySettings'),
     1072                                dragInfo:   state.get('dragInfo'),
     1073
     1074                                AttachmentView: state.get('AttachmentView')
     1075                        });
     1076                },
     1077
     1078                cropAvatar: function( content ) {
     1079                        var state = this._frame.state();
     1080
     1081                        this._frame.$el.removeClass('hide-toolbar');
     1082
     1083                        // Browse our library of attachments.
     1084                        content.view = new BPmedia.view.AvatarCroper({
     1085                                controller: this._frame,
     1086                                collection: state.get('library'),
     1087                                selection:  state.get('selection'),
     1088                                model:      state,
     1089                                display:    state.get('displaySettings'),
     1090                                dragInfo:   state.get('dragInfo')
     1091                        });
     1092                },
     1093
     1094                createSelectToolbar: function( toolbar, options ) {
     1095                        options = options || this._frame.options.button || {};
     1096                        options.controller = this._frame;
     1097
     1098                        if ( 'avatar' == bp.media.params.item_type ) {
     1099                                toolbar.view = new bp.media.view.Toolbar.Select( options );
     1100                        }
     1101
     1102                        if ( ! _.isUndefined( bp.media.params.callback ) && bp.media.params.callback ) {
     1103                                toolbar.view = new bp.media.view.ToolbarSelect( options );
     1104                        }
     1105                },
     1106
     1107                uploadContent: function() {
     1108
     1109                        if ( 'avatar' == bp.media.params.item_type ) {
     1110                                this._frame.reset();
     1111                                this._frame.state().get('library').reset();
     1112                        }
     1113
     1114                        this._frame.$el.removeClass('hide-toolbar');
     1115                        this._frame.content.set( new bp.media.UploaderInline({
     1116                                controller: this._frame
     1117                        }) );
     1118                },
     1119
     1120                open: function() {
     1121                        $( '.media-modal' ).css({
     1122                                "top":    "10%",
     1123                                "right":  "15%",
     1124                                "bottom": "10%",
     1125                                "left":   "15%"
     1126                    });
     1127
     1128                        // Hide screen reader text on front end
     1129                        if ( ! $( 'body' ).hasClass('wp-admin' ) ) {
     1130                                $( 'a.media-modal-close .screen-reader-text' ).hide();
     1131                        }
     1132                },
     1133
     1134                close: function() {
     1135                        $( '.media-modal' ).removeAttr( 'style');
     1136                },
     1137
     1138                select: function() {
     1139                        var settings = bp.media.view.settings.bp_attachments,
     1140                                selection = this.get( 'selection' ).single();
     1141
     1142                        $( '.added' ).remove();
     1143                        BPmedia.set( selection );
     1144                },
     1145
     1146                set: function( attachment ) {
     1147                        if ( 'avatar' == bp.media.params.item_type ) {
     1148                                bp.media.post( 'bp_attachments_set_avatar', {
     1149                                        json:          true,
     1150                                        object:        bp.media.view.settings.bp_attachments.object,
     1151                                        component:     bp.media.params.item_component,
     1152                                        avatar_dir:    bp.media.view.settings.bp_attachments.avatar_dir,
     1153                                        item_id:       bp.media.view.settings.bp_attachments.item_id,
     1154                                        original_file: attachment.get( 'src' ),
     1155                                        crop_w:        attachment.get( 'w' ),
     1156                                        crop_h:        attachment.get( 'h' ),
     1157                                        crop_x:        attachment.get( 'x' ),
     1158                                        crop_y:        attachment.get( 'y' ),
     1159                                        nonce:         bp.media.params.nonce
     1160                                }).done( function( html ) {
     1161                                        $( '#' + bp.media.params.item_component + '-avatar', '#bp_' + bp.media.params.item_component + '_avatar' ).html( html );
     1162                                        $( bp.media.view.settings.bp_attachments.button_id ).hide();
     1163                                });
     1164                                // reseting the frame
     1165                                this._frame.reset();
     1166                                this._frame.state().get('library').reset();
     1167
     1168                        } else {
     1169
     1170                                if ( -1 == bp.media.params.callback.indexOf( 'http://' ) ) {
     1171                                        bp.media.post( bp.media.params.callback, {
     1172                                                json:          true,<