<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.1//EN"
"http://www.w3.org/TR/xhtml11/DTD/xhtml11.dtd">
<html xmlns="http://www.w3.org/1999/xhtml">
<head><meta http-equiv="content-type" content="text/html; charset=utf-8" />
<title>[BuddyPress][9622] trunk/src/bp-core: Introduces the BuddyPress Attachments API</title>
</head>
<body>

<style type="text/css"><!--
#msg dl.meta { border: 1px #006 solid; background: #369; padding: 6px; color: #fff; }
#msg dl.meta dt { float: left; width: 6em; font-weight: bold; }
#msg dt:after { content:':';}
#msg dl, #msg dt, #msg ul, #msg li, #header, #footer, #logmsg { font-family: verdana,arial,helvetica,sans-serif; font-size: 10pt;  }
#msg dl a { font-weight: bold}
#msg dl a:link    { color:#fc3; }
#msg dl a:active  { color:#ff0; }
#msg dl a:visited { color:#cc6; }
h3 { font-family: verdana,arial,helvetica,sans-serif; font-size: 10pt; font-weight: bold; }
#msg pre { overflow: auto; background: #ffc; border: 1px #fa0 solid; padding: 6px; }
#logmsg { background: #ffc; border: 1px #fa0 solid; padding: 1em 1em 0 1em; }
#logmsg p, #logmsg pre, #logmsg blockquote { margin: 0 0 1em 0; }
#logmsg p, #logmsg li, #logmsg dt, #logmsg dd { line-height: 14pt; }
#logmsg h1, #logmsg h2, #logmsg h3, #logmsg h4, #logmsg h5, #logmsg h6 { margin: .5em 0; }
#logmsg h1:first-child, #logmsg h2:first-child, #logmsg h3:first-child, #logmsg h4:first-child, #logmsg h5:first-child, #logmsg h6:first-child { margin-top: 0; }
#logmsg ul, #logmsg ol { padding: 0; list-style-position: inside; margin: 0 0 0 1em; }
#logmsg ul { text-indent: -1em; padding-left: 1em; }#logmsg ol { text-indent: -1.5em; padding-left: 1.5em; }
#logmsg > ul, #logmsg > ol { margin: 0 0 1em 0; }
#logmsg pre { background: #eee; padding: 1em; }
#logmsg blockquote { border: 1px solid #fa0; border-left-width: 10px; padding: 1em 1em 0 1em; background: white;}
#logmsg dl { margin: 0; }
#logmsg dt { font-weight: bold; }
#logmsg dd { margin: 0; padding: 0 0 0.5em 0; }
#logmsg dd:before { content:'\00bb';}
#logmsg table { border-spacing: 0px; border-collapse: collapse; border-top: 4px solid #fa0; border-bottom: 1px solid #fa0; background: #fff; }
#logmsg table th { text-align: left; font-weight: normal; padding: 0.2em 0.5em; border-top: 1px dotted #fa0; }
#logmsg table td { text-align: right; border-top: 1px dotted #fa0; padding: 0.2em 0.5em; }
#logmsg table thead th { text-align: center; border-bottom: 1px solid #fa0; }
#logmsg table th.Corner { text-align: left; }
#logmsg hr { border: none 0; border-top: 2px dashed #fa0; height: 1px; }
#header, #footer { color: #fff; background: #636; border: 1px #300 solid; padding: 6px; }
#patch { width: 100%; }
#patch h4 {font-family: verdana,arial,helvetica,sans-serif;font-size:10pt;padding:8px;background:#369;color:#fff;margin:0;}
#patch .propset h4, #patch .binary h4 {margin:0;}
#patch pre {padding:0;line-height:1.2em;margin:0;}
#patch .diff {width:100%;background:#eee;padding: 0 0 10px 0;overflow:auto;}
#patch .propset .diff, #patch .binary .diff  {padding:10px 0;}
#patch span {display:block;padding:0 10px;}
#patch .modfile, #patch .addfile, #patch .delfile, #patch .propset, #patch .binary, #patch .copfile {border:1px solid #ccc;margin:10px 0;}
#patch ins {background:#dfd;text-decoration:none;display:block;padding:0 10px;}
#patch del {background:#fdd;text-decoration:none;display:block;padding:0 10px;}
#patch .lines, .info {color:#888;background:#fff;}
--></style>
<div id="msg">
<dl class="meta" style="font-size: 105%">
<dt style="float: left; width: 6em; font-weight: bold">Revision</dt> <dd><a style="font-weight: bold" href="http://buddypress.trac.wordpress.org/changeset/9622">9622</a><script type="application/ld+json">{"@context":"http://schema.org","@type":"EmailMessage","description":"Review this Commit","action":{"@type":"ViewAction","url":"http://buddypress.trac.wordpress.org/changeset/9622","name":"Review Commit"}}</script></dd>
<dt style="float: left; width: 6em; font-weight: bold">Author</dt> <dd>imath</dd>
<dt style="float: left; width: 6em; font-weight: bold">Date</dt> <dd>2015-03-17 18:05:20 +0000 (Tue, 17 Mar 2015)</dd>
</dl>

<pre style='padding-left: 1em; margin: 2em 0; border-left: 2px solid #ccc; line-height: 1.25; font-size: 105%; font-family: sans-serif'>Introduces the BuddyPress Attachments API

`BP_Attachment` is a new class custom and core components can extend to manage file uploads.

- It is taking care of setting the uploads data (path, url..)
- It is creating the specific base uploads directory for the component if requested
- It is taking care of uploading the files into the uploads directory
- Custom validation rules can be set in order to for instance: restrict mime types or file size
- If, like avatars, cropping an image file is needed, the class includes a crop method to help you.

`bp_upload_dir()` is also a new function to make sure the uploads data will always be the one of the site BuddyPress is activated on (the root site).

Props johnjamesjacoby, boonebgorges

See <a href="http://buddypress.trac.wordpress.org/ticket/6278">#6278</a></pre>

<h3>Modified Paths</h3>
<ul>
<li><a href="#trunksrcbpcorebpcoreclassesphp">trunk/src/bp-core/bp-core-classes.php</a></li>
<li><a href="#trunksrcbpcorebpcorefunctionsphp">trunk/src/bp-core/bp-core-functions.php</a></li>
</ul>

<h3>Added Paths</h3>
<ul>
<li><a href="#trunksrcbpcoreclassesclassbpattachmentphp">trunk/src/bp-core/classes/class-bp-attachment.php</a></li>
</ul>

</div>
<div id="patch">
<h3>Diff</h3>
<a id="trunksrcbpcorebpcoreclassesphp"></a>
<div class="modfile"><h4 style="background-color: #eee; color: inherit; margin: 1em 0; padding: 1.3em; font-size: 115%">Modified: trunk/src/bp-core/bp-core-classes.php</h4>
<pre class="diff"><span>
<span class="info" style="display: block; padding: 0 10px; color: #888">--- trunk/src/bp-core/bp-core-classes.php     2015-03-16 18:59:09 UTC (rev 9621)
+++ trunk/src/bp-core/bp-core-classes.php       2015-03-17 18:05:20 UTC (rev 9622)
</span><span class="lines" style="display: block; padding: 0 10px; color: #888">@@ -21,3 +21,4 @@
</span><span class="cx" style="display: block; padding: 0 10px"> require dirname( __FILE__ ) . '/classes/class-bp-members-suggestions.php';
</span><span class="cx" style="display: block; padding: 0 10px"> require dirname( __FILE__ ) . '/classes/class-bp-recursive-query.php';
</span><span class="cx" style="display: block; padding: 0 10px"> require dirname( __FILE__ ) . '/classes/class-bp-media-extractor.php';
</span><ins style="background-color: #dfd; text-decoration:none; display:block; padding: 0 10px">+require dirname( __FILE__ ) . '/classes/class-bp-attachment.php';
</ins></span></pre></div>
<a id="trunksrcbpcorebpcorefunctionsphp"></a>
<div class="modfile"><h4 style="background-color: #eee; color: inherit; margin: 1em 0; padding: 1.3em; font-size: 115%">Modified: trunk/src/bp-core/bp-core-functions.php</h4>
<pre class="diff"><span>
<span class="info" style="display: block; padding: 0 10px; color: #888">--- trunk/src/bp-core/bp-core-functions.php   2015-03-16 18:59:09 UTC (rev 9621)
+++ trunk/src/bp-core/bp-core-functions.php     2015-03-17 18:05:20 UTC (rev 9622)
</span><span class="lines" style="display: block; padding: 0 10px; color: #888">@@ -2147,3 +2147,50 @@
</span><span class="cx" style="display: block; padding: 0 10px"> 
</span><span class="cx" style="display: block; padding: 0 10px">        return apply_filters( 'bp_core_get_suggestions', $retval, $args );
</span><span class="cx" style="display: block; padding: 0 10px"> }
</span><ins style="background-color: #dfd; text-decoration:none; display:block; padding: 0 10px">+
+/**
+ * Set data from the BP root blog's upload directory.
+ *
+ * Handy for multisite instances because all uploads are made on the BP root
+ * blog and we need to query the BP root blog for the upload directory data.
+ *
+ * This function ensures that we only need to use {@link switch_to_blog()}
+ * once to get what we need.
+ *
+ * @since BuddyPress (2.3.0)
+ *
+ * @uses  is_multisite()
+ * @uses  bp_is_root_blog()
+ * @uses  switch_to_blog()
+ * @uses  wp_upload_dir()
+ * @uses  restore_current_blog()
+ */
+function bp_upload_dir() {
+       $bp = buddypress();
+
+       if ( empty( $bp->upload_dir ) ) {
+               $need_switch = (bool) ( is_multisite() && ! bp_is_root_blog() );
+
+               // Maybe juggle to root blog
+               if ( true === $need_switch ) {
+                       switch_to_blog( bp_get_root_blog_id() );
+               }
+
+               // Get the upload directory (maybe for root blog)
+               $wp_upload_dir = wp_upload_dir();
+
+               // Maybe juggle back to current blog
+               if ( true === $need_switch ) {
+                       restore_current_blog();
+               }
+
+               // Bail if an error occurred
+               if ( ! empty( $wp_upload_dir['error'] ) ) {
+                       return false;
+               }
+
+               $bp->upload_dir = $wp_upload_dir;
+       }
+
+       return $bp->upload_dir;
+}
</ins></span></pre></div>
<a id="trunksrcbpcoreclassesclassbpattachmentphp"></a>
<div class="addfile"><h4 style="background-color: #eee; color: inherit; margin: 1em 0; padding: 1.3em; font-size: 115%">Added: trunk/src/bp-core/classes/class-bp-attachment.php</h4>
<pre class="diff"><span>
<span class="info" style="display: block; padding: 0 10px; color: #888">--- trunk/src/bp-core/classes/class-bp-attachment.php                         (rev 0)
+++ trunk/src/bp-core/classes/class-bp-attachment.php   2015-03-17 18:05:20 UTC (rev 9622)
</span><span class="lines" style="display: block; padding: 0 10px; color: #888">@@ -0,0 +1,508 @@
</span><ins style="background-color: #dfd; text-decoration:none; display:block; padding: 0 10px">+<?php
+/**
+ * Core attachment class.
+ *
+ * @package BuddyPress
+ * @subpackage Core
+ */
+
+// Exit if accessed directly
+defined( 'ABSPATH' ) || exit;
+
+/**
+ * BP Attachment class
+ *
+ * Extend it to manage your component's uploads.
+ *
+ * @since BuddyPress (2.3.0)
+ */
+class BP_Attachment {
+
+       /** Upload properties *****************************************************/
+
+       /**
+        * The file being uploaded
+        *
+        * @var array
+        */
+       public $attachment = array();
+
+       /**
+        * Maximum file size in kilobytes
+        *
+        * @var int
+        */
+       public $original_max_filesize = 0;
+
+       /**
+        * List of allowed file extensions
+        * Defaults to get_allowed_mime_types()
+        *
+        * @var int
+        */
+       public $allowed_mime_types = array();
+
+       /**
+        * component's upload base directory.
+        *
+        * @var string
+        */
+       public $base_dir = '';
+
+       /**
+        * The upload action.
+        *
+        * @var string
+        */
+       public $action = '';
+
+       /**
+        * The file input name attribute
+        *
+        * @var string
+        */
+       public $file_input = '';
+
+       /**
+        * List of upload errors.
+        *
+        * @var array
+        */
+       public $upload_error_strings = array();
+
+       /**
+        * List of required core files
+        *
+        * @var array
+        */
+       public $required_wp_files = array( 'file' );
+
+       /**
+        * Construct Upload parameters
+        *
+        * @since BuddyPress (2.3.0)
+        *
+        * @param array $args {
+        *     @type int    $original_max_filesize Maximum file size in kilobytes. Defaults to php.ini settings.
+        *     @type array  $allowed_mime_types    List of allowed file extensions (eg: array( 'jpg', 'gif', 'png' ) ).
+        *                                         Defaults to WordPress allowed mime types
+        *     @type string $base_dir              Component's upload base directory. Defaults to WordPress 'uploads'
+        *     @type string $action                The upload action used when uploading a file, $_POST['action'] must be set
+        *                                         and its value must equal $action {@link wp_handle_upload()} (required)
+        *     @type string $file_input            The name attribute used in the file input. (required)
+        *     @type array  $upload_error_strings  A list of specific error messages (optional).
+        *     @type array  $required_wp_files     The list of required WordPress core files. Default: array( 'file' );
+        * }
+        * @uses  sanitize_key()
+        * @uses  wp_max_upload_size()
+        * @uses  bp_parse_args()
+        * @uses  BP_Attachment->set_upload_error_strings()
+        * @uses  BP_Attachment->set_upload_dir()
+        */
+       public function __construct( $args = '' ) {
+               // Upload action and the file input name are required parameters
+               if ( empty( $args['action'] ) || empty( $args['file_input'] ) ) {
+                       return false;
+               }
+
+               // Sanitize the action ID and the file input name
+               $this->action     = sanitize_key( $args['action'] );
+               $this->file_input = sanitize_key( $args['file_input'] );
+
+               /**
+                * Max file size defaults to php ini settings or, in the case of
+                * a multisite config, the root site fileupload_maxk option
+                */
+               $this->original_max_filesize = (int) wp_max_upload_size();
+
+               $params = bp_parse_args( $args, get_class_vars( __CLASS__ ), $this->action . '_upload_params' );
+
+               foreach ( $params as $key => $param ) {
+                       if ( 'upload_error_strings' === $key ) {
+                               $this->{$key} = $this->set_upload_error_strings( $param );
+                       } else {
+                               $this->{$key} = $param;
+                       }
+               }
+
+               // Set the path/url and base dir for uploads
+               $this->set_upload_dir();
+       }
+
+       /**
+        * Set upload path and url for the component.
+        *
+        * @since BuddyPress (2.3.0)
+        *
+        * @uses  bp_upload_dir()
+        */
+       public function set_upload_dir() {
+               // Set the directory, path, & url variables
+               $this->upload_dir  = bp_upload_dir();
+
+               if ( empty( $this->upload_dir ) ) {
+                       return false;
+               }
+
+               $this->upload_path = $this->upload_dir['basedir'];
+               $this->url         = $this->upload_dir['baseurl'];
+
+               // Ensure URL is https if SSL is set/forced
+               if ( is_ssl() ) {
+                       $this->url = str_replace( 'http://', 'https://', $this->url );
+               }
+
+               /**
+                * Custom base dir.
+                *
+                * If the component set this property, set the specific path, url and create the dir
+                */
+               if ( ! empty( $this->base_dir ) ) {
+                       $this->upload_path = trailingslashit( $this->upload_path ) . $this->base_dir;
+                       $this->url         = trailingslashit( $this->url  ) . $this->base_dir;
+
+                       // Finally create the base dir
+                       $this->create_dir();
+               }
+       }
+
+       /**
+        * Set Upload error messages
+        *
+        * Used into the $overrides argument of BP_Attachment->upload()
+        *
+        * @since BuddyPress (2.3.0)
+        *
+        * @param array $param a list of error messages to add to BuddyPress core ones
+        * @return array the list of upload errors
+        */
+       public function set_upload_error_strings( $param = array() ) {
+               /**
+                * Index of the array is the error code
+                * Custom errors will start at 9 code
+                */
+               $upload_errors = array(
+                       0 => __( 'The file was uploaded successfully', 'buddypress' ),
+                       1 => __( 'The uploaded file exceeds the maximum allowed file size for this site', 'buddypress' ),
+                       2 => sprintf( __( 'The uploaded file exceeds the maximum allowed file size of: %s', 'buddypress' ), size_format( $this->original_max_filesize ) ),
+                       3 => __( 'The uploaded file was only partially uploaded.', 'buddypress' ),
+                       4 => __( 'No file was uploaded.', 'buddypress' ),
+                       5 => '',
+                       6 => __( 'Missing a temporary folder.', 'buddypress' ),
+                       7 => __( 'Failed to write file to disk.', 'buddypress' ),
+                       8 => __( 'File upload stopped by extension.', 'buddypress' ),
+               );
+
+               if ( ! array_intersect_key( $upload_errors, (array) $param ) ) {
+                       foreach ( $param as $key_error => $error_message ) {
+                               $upload_errors[ $key_error ] = $error_message;
+                       }
+               }
+
+               return $upload_errors;
+       }
+
+       /**
+        * Include the WordPress core needed files
+        *
+        * @since BuddyPress (2.3.0)
+        */
+       public function includes() {
+               foreach ( array_unique( $this->required_wp_files ) as $wp_file ) {
+                       if ( ! file_exists( ABSPATH . "/wp-admin/includes/{$wp_file}.php" ) ) {
+                               continue;
+                       }
+
+                       require_once( ABSPATH . "/wp-admin/includes/{$wp_file}.php" );
+               }
+       }
+
+       /**
+        * Upload the attachment
+        *
+        * @since BuddyPress (2.3.0)
+        *
+        * @param  array $file               The appropriate entry the from $_FILES superglobal.
+        * @param  string $upload_dir_filter A specific filter to be applied to 'upload_dir' (optional).
+        * @uses   wp_handle_upload()        To upload the file
+        * @uses   add_filter()              To temporarly overrides WordPress uploads data
+        * @uses   remove_filter()           To stop overriding WordPress uploads data
+        * @uses   apply_filters()           Call 'bp_attachment_upload_overrides' to include specific upload overrides
+        *
+        * @return array                     On success, returns an associative array of file attributes.
+        *                                   On failure, returns an array containing the error message
+        *                                   (eg: array( 'error' => $message ) )
+        */
+       public function upload( $file, $upload_dir_filter = '' ) {
+               /**
+                * Upload action and the file input name are required parameters
+                * @see BP_Attachment:__construct()
+                */
+               if ( empty( $this->action ) || empty( $this->file_input ) ) {
+                       return false;
+               }
+
+               /**
+                * Add custom rules before enabling the file upload
+                */
+               add_filter( "{$this->action}_prefilter", array( $this, 'validate_upload' ), 10, 1 );
+
+               // Set Default overrides
+               $overrides = array(
+                       'action'               => $this->action,
+                       'upload_error_strings' => $this->upload_error_strings,
+               );
+
+               /**
+                * Add a mime override if needed
+                * Used to restrict uploads by extensions
+                */
+               if ( ! empty( $this->allowed_mime_types ) ) {
+                       $mime_types = $this->validate_mime_types();
+
+                       if ( ! empty( $mime_types ) ) {
+                               $overrides['mimes'] = $mime_types;
+                       }
+               }
+
+               /**
+                * If you need to add some overrides we haven't thought of
+                *
+                * @var  array $overrides the wp_handle_upload overrides
+                */
+               $overrides = apply_filters( 'bp_attachment_upload_overrides', $overrides );
+
+               $this->includes();
+
+               /**
+                * If the $base_dir was set when constructing the class,
+                * and no specific filter has been requested, use a default
+                * filter to create the specific $base dir
+                * @see  BP_Attachment->upload_dir_filter()
+                */
+               if ( empty( $upload_dir_filter ) && ! empty( $this->base_dir ) ) {
+                       $upload_dir_filter = array( $this, 'upload_dir_filter' );
+               }
+
+               // Make sure the file will be uploaded in the attachment directory
+               add_filter( 'upload_dir', $upload_dir_filter, 10, 0 );
+
+               // Upload the attachment
+               $this->attachment = wp_handle_upload( $file[ $this->file_input ], $overrides );
+
+               // Restore WordPress Uploads data
+               remove_filter( 'upload_dir', $upload_dir_filter, 10, 0 );
+
+               // Finally return the uploaded file or the error
+               return $this->attachment;
+       }
+
+       /**
+        * Validate the allowed mime types using WordPress allowed mime types
+        *
+        * In case of a multisite, the mime types are already restricted by
+        * the 'upload_filetypes' setting. BuddyPress will respect this setting.
+        * @see check_upload_mimes()
+        *
+        * @since BuddyPress (2.3.0)
+        *
+        * @uses get_allowed_mime_types()
+        */
+       protected function validate_mime_types() {
+               $wp_mimes = get_allowed_mime_types();
+               $valid_mimes = array();
+
+               // Set the allowed mimes for the upload
+               foreach ( (array) $this->allowed_mime_types as $ext ) {
+                       foreach ( $wp_mimes as $ext_pattern => $mime ) {
+                               if ( $ext != '' && strpos( $ext_pattern, $ext ) !== false ) {
+                                       $valid_mimes[$ext_pattern] = $mime;
+                               }
+                       }
+               }
+               return $valid_mimes;
+       }
+
+       /**
+        * Specific upload rules
+        *
+        * Override this function from your child class to build your specific rules
+        * By default, if an original_max_filesize is provided, a check will be done
+        * on the file size.
+        *
+        * @see BP_Attachment_Avatar->validate_upload() for an example of use
+        *
+        * @since BuddyPress (2.3.0)
+        *
+        * @param  array $file the temporary file attributes (before it has been moved)
+        * @return array the file
+        */
+       public function validate_upload( $file = array() ) {
+               // Bail if already an error
+               if ( ! empty( $file['error'] ) ) {
+                       return $file;
+               }
+
+               if ( ! empty( $this->original_max_filesize ) && $file['size'] > $this->original_max_filesize ) {
+                       $file['error'] = 2;
+               }
+
+               // Return the file
+               return $file;
+       }
+
+       /**
+        * Default filter to save the attachments
+        *
+        * @since BuddyPress (2.3.0)
+        *
+        * @uses   apply_filters() call 'bp_attachment_upload_dir' to eventually override the upload location
+        *                         regarding to context
+        * @return array the upload directory data
+        */
+       public function upload_dir_filter() {
+               /**
+                * Filters the component's upload directory.
+                *
+                * @since BuddyPress (2.3.0)
+                *
+                * @param array $value Array containing the path, URL, and other helpful settings.
+                */
+               return apply_filters( 'bp_attachment_upload_dir', array(
+                       'path'    => $this->upload_path,
+                       'url'     => $this->url,
+                       'subdir'  => false,
+                       'basedir' => $this->upload_path,
+                       'baseurl' => $this->url,
+                       'error'   => false
+               ) );
+       }
+
+       /**
+        * Create the custom base directory for the component uploads
+        *
+        * Override this function in your child class to run specific actions
+        * (eg: add an .htaccess file)
+        *
+        * @since BuddyPress (2.3.0)
+        *
+        * @uses   wp_mkdir_p()
+        */
+       public function create_dir() {
+               // Bail if no specific base dir is set
+               if ( empty( $this->base_dir ) ) {
+                       return false;
+               }
+
+               // Check if upload path already exists
+               if ( ! file_exists( $this->upload_path ) ) {
+
+                       // If path does not exist, attempt to create it
+                       if ( ! wp_mkdir_p( $this->upload_path ) ) {
+                               return false;
+                       }
+               }
+
+               // Directory exists
+               return true;
+       }
+
+       /**
+        * Crop an image file
+        *
+        * @since BuddyPress (2.3.0)
+        *
+        * @param array $args {
+        *     @type string $original_file The source file (absolute path) for the Attachment.
+        *     @type int    $crop_x The start x position to crop from.
+        *     @type int    $crop_y The start y position to crop from.
+        *     @type int    $crop_w The width to crop.
+        *     @type int    $crop_h The height to crop.
+        *     @type int    $dst_w The destination width.
+        *     @type int    $dst_h The destination height.
+        *     @type int    $src_abs Optional. If the source crop points are absolute.
+        *     @type string $dst_file Optional. The destination file to write to.
+        * }
+        * @uses wp_crop_image()
+        * @return string|WP_Error New filepath on success, WP_Error on failure.
+        */
+       public function crop( $args = array() ) {
+               $wp_error = new WP_Error();
+
+               $r = wp_parse_args( $args, array(
+                       'original_file' => '',
+                       'crop_x'        => 0,
+                       'crop_y'        => 0,
+                       'crop_w'        => 0,
+                       'crop_h'        => 0,
+                       'dst_w'         => 0,
+                       'dst_h'         => 0,
+                       'src_abs'       => false,
+                       'dst_file'      => false,
+               ) );
+
+               if ( empty( $r['original_file'] ) || ! file_exists( $r['original_file'] ) ) {
+                       $wp_error->add( 'crop_error', __( 'Cropping the file failed: missing source file.', 'buddypress' ) );
+                       return $wp_error;
+               }
+
+               // Check image file pathes
+               $path_error = __( 'Cropping the file failed: the file path is not allowed.', 'buddypress' );
+
+               // Make sure it's coming from an uploaded file
+               if ( false === strpos( $r['original_file'], $this->upload_path ) ) {
+                       $wp_error->add( 'crop_error', $path_error );
+                       return $wp_error;
+               }
+
+               /**
+                * If no destination file is provided, WordPress will use a default name
+                * and will write the file in the source file's folder.
+                * If a destination file is provided, we need to make sure it's going into uploads
+                */
+               if ( ! empty( $r['dst_file'] ) && false === strpos( $r['dst_file'], $this->upload_path ) ) {
+                       $wp_error->add( 'crop_error', $path_error );
+                       return $wp_error;
+               }
+
+               // Check image file types
+               $check_types = array( 'src_file' => array( 'file' => $r['original_file'], 'error' => _x( 'source file', 'Attachment source file', 'buddypress' ) ) );
+               if ( ! empty( $r['dst_file'] ) ) {
+                       $check_types['dst_file'] = array( 'file' => $r['dst_file'], 'error' => _x( 'destination file', 'Attachment destination file', 'buddypress' ) );
+               }
+
+               /**
+                * WordPress image supported types
+                * @see wp_attachment_is()
+                */
+               $supported_image_types = array(
+                       'jpg'  => 1,
+                       'jpeg' => 1,
+                       'jpe'  => 1,
+                       'gif'  => 1,
+                       'png'  => 1,
+               );
+
+               foreach ( $check_types as $file ) {
+                       $is_image      = wp_check_filetype( $file['file'] );
+                       $ext           = $is_image['ext'];
+
+                       if ( empty( $ext ) || empty( $supported_image_types[ $ext ] ) ) {
+                               $wp_error->add( 'crop_error', sprintf( __( 'Cropping the file failed: %s is not a supported image file.', 'buddypress' ), $file['error'] ) );
+                               return $wp_error;
+                       }
+               }
+
+               // Add the image.php to the required WordPress files, if it's not already the case
+               $required_files = array_flip( $this->required_wp_files );
+               if ( ! isset( $required_files['image'] ) ) {
+                       $this->required_wp_files[] = 'image';
+               }
+
+               // Load the files
+               $this->includes();
+
+               // Finally crop the image
+               return wp_crop_image( $r['original_file'], (int) $r['crop_x'], (int) $r['crop_y'], (int) $r['crop_w'], (int) $r['crop_h'], (int) $r['dst_w'], (int) $r['dst_h'], $r['src_abs'], $r['dst_file'] );
+       }
+}
</ins></span></pre>
</div>
</div>

</body>
</html>