name = 'image_crop'; $this->label = __('Image with user-crop'); $this->category = __("Content",'acf'); // Basic, Content, Choice, etc $this->defaults = array( 'force_crop' => 'no', 'crop_type' => 'hard', 'preview_size' => 'medium', 'save_format' => 'id', 'save_in_media_library' => 'yes', 'target_size' => 'thumbnail', 'retina_mode' => 'no' // add default here to merge into your field. // This makes life easy when creating the field options as you don't need to use any if( isset('') ) logic. eg: //'preview_size' => 'thumbnail' ); $this->options = get_option( 'acf_image_crop_settings' ); //Call grandparent cunstructor acf_field::__construct(); // settings $this->settings = array( 'path' => apply_filters('acf/helpers/get_path', __FILE__), 'dir' => apply_filters('acf/helpers/get_dir', __FILE__), 'version' => '1.0.0' ); // add ajax action to be able to retrieve full image size via javascript add_action( 'wp_ajax_acf_image_crop_get_image_size', array( &$this, 'crop_get_image_size' ) ); add_action( 'wp_ajax_acf_image_crop_perform_crop', array( &$this, 'perform_crop' ) ); // add filter to media query function to hide cropped images from media library add_filter('ajax_query_attachments_args', array($this, 'filterMediaQuery')); // Register extra fields on the media settings page on admin_init add_action('admin_init', array($this, 'registerSettings')); } // AJAX handler for retieving full image dimensions from ID public function crop_get_image_size() { $img = wp_get_attachment_image_src( $_POST['image_id'], 'full'); if($img){ echo json_encode( array( 'url' => $img[0], 'width' => $img[1], 'height' => $img[2] ) ); } exit; } /* * create_options() * * Create extra options for your field. This is rendered when editing a field. * The value of $field['name'] can be used (like bellow) to save extra data to the $field * * @type action * @since 3.6 * @date 23/01/13 * * @param $field - an array holding all the field's data */ function create_options($field) { // defaults? /* $field = array_merge($this->defaults, $field); */ // key is needed in the field names to correctly save the data $key = $field['name']; // Create Field Options HTML ?>

'select', 'name' => 'fields[' . $key . '][crop_type]', 'value' => $field['crop_type'], 'class' => 'crop-type-select', 'choices' => array( 'hard' => __('Hard crop', 'acf'), 'min' => __('Minimal dimensions', 'acf') ) )); ?>

__("Custom size",'acf'))); unset($sizes['full']); do_action('acf/create_field', array( 'type' => 'select', 'name' => 'fields[' . $key . '][target_size]', 'value' => $field['target_size'], 'class' => 'target-size-select', 'choices' => $sizes )); ?>

'text', 'name' => 'fields[' . $key . '][width]', 'value' => isset($field['width']) ? $field['width'] : '', 'class' => 'width dimension', 'placeholder' => 'Width' )); ?>
 : 
'text', 'name' => 'fields[' . $key . '][height]', 'value' => isset($field['height']) ? $field['height'] : '', 'class' => 'height dimension', 'placeholder' => 'Height' )); ?>

'select', 'name' => 'fields[' . $key . '][preview_size]', 'value' => $field['preview_size'], 'class' => 'preview-size-select', 'choices' => apply_filters('acf/get_image_sizes', array()) )); ?>

'radio', 'name' => 'fields['.$key.'][force_crop]', 'value' => $field['force_crop'], 'layout' => 'horizontal', 'choices' => array( 'yes' => __("Yes",'acf'), 'no' => __("No",'acf') ) )); ?>

'radio', 'name' => 'fields['.$key.'][save_in_media_library]', 'value' => $field['save_in_media_library'], 'layout' => 'horizontal', 'choices' => array( 'yes' => __("Yes",'acf'), 'no' => __("No",'acf') ), 'class' => 'save-in-media-library-select' )); ?> getOption('retina_mode')){ $retina_instructions .= '
' . __('NB. You currently have enabled retina mode globally for all fields through settings, which will override this setting.','acf-image_crop'); } ?>

'radio', 'name' => 'fields['.$key.'][retina_mode]', 'value' => $field['retina_mode'], 'layout' => 'horizontal', 'choices' => array( 'yes' => __("Yes",'acf'), 'no' => __("No",'acf') ), )); ?>

'radio', 'name' => 'fields['.$key.'][save_format]', 'value' => $field['save_format'], 'layout' => 'horizontal', 'choices' => array( 'url' => __("Image URL",'acf'), 'id' => __("Image ID",'acf'), 'object' => __("Image Object",'acf') ), 'class' => 'return-value-select' )); ?> defaults, $field); */ // perhaps use $field['preview_size'] to alter the markup? $data = json_decode($field['value']); // create Field HTML // vars $o = array( 'class' => '', 'url' => '', ); // $originalImage = null; // if( $data && is_object($data) && is_numeric($data->original_image) ) // { // $originalImage = wp_get_attachment_image_src($data->original_image, 'full'); // $url = wp_get_attachment_image_src($data->original_image, $field['preview_size']); // if($field['save_in_media_library'] == 'yes'){ // if(is_numeric($data->cropped_image)){ // $url = wp_get_attachment_image_src($data->cropped_image, $field['preview_size']); // } // } // else{ // if($data->cropped_image_url){ // $url = $data->cropped_image_url; // } // else{ // } // } // $o['class'] = 'active'; // $o['url'] = $url[0]; // } $imageData = $this->get_image_data($field); //print_r($field); $originalImage = wp_get_attachment_image_src($imageData->original_image, 'full'); if($imageData->original_image){ $o['class'] = 'active'; $o['url'] = $imageData->preview_image_url; } $width = 0; $height = 0; if($field['target_size'] == 'custom'){ $width = $field['width']; $height = $field['height']; } else{ global $_wp_additional_image_sizes; $s = $field['target_size']; if (isset($_wp_additional_image_sizes[$s])) { $width = intval($_wp_additional_image_sizes[$s]['width']); $height = intval($_wp_additional_image_sizes[$s]['height']); } else { $width = get_option($s.'_size_w'); $height = get_option($s.'_size_h'); } } // Retina mode if($this->getOption('retina_mode') || $field['retina_mode'] == 'yes'){ $width = $width * 2; $height = $height * 2; } ?>
data-save-to-media-library="" >

Crop the image

original_image ): ?>

Preview

Crop

cropped_image; } else{ // We are migrating from a standard image field $data = new stdClass(); $data->cropped_image = $value; $data->original_image = $value; } // format if( $field['save_format'] == 'url' ) { if(is_numeric($data->cropped_image)){ $value = wp_get_attachment_url( $data->cropped_image ); } elseif(is_array($data->cropped_image)){ $value = $this->getAbsoluteImageUrl($data->cropped_image['image']); } elseif(is_object($data->cropped_image)){ $value = $this->getAbsoluteImageUrl($data->cropped_image->image); } } elseif( $field['save_format'] == 'object' ) { if(is_numeric($data->cropped_image )){ $value = $this->getImageArray($data->cropped_image); $value['original_image'] = $this->getImageArray($data->original_image); // $attachment = get_post( $data->cropped_image ); // // validate // if( !$attachment ) // { // return false; // } // // create array to hold value data // $src = wp_get_attachment_image_src( $attachment->ID, 'full' ); // $value = array( // 'id' => $attachment->ID, // 'alt' => get_post_meta($attachment->ID, '_wp_attachment_image_alt', true), // 'title' => $attachment->post_title, // 'caption' => $attachment->post_excerpt, // 'description' => $attachment->post_content, // 'mime_type' => $attachment->post_mime_type, // 'url' => $src[0], // 'width' => $src[1], // 'height' => $src[2], // 'sizes' => array(), // ); // // find all image sizes // $image_sizes = get_intermediate_image_sizes(); // if( $image_sizes ) // { // foreach( $image_sizes as $image_size ) // { // // find src // $src = wp_get_attachment_image_src( $attachment->ID, $image_size ); // // add src // $value[ 'sizes' ][ $image_size ] = $src[0]; // $value[ 'sizes' ][ $image_size . '-width' ] = $src[1]; // $value[ 'sizes' ][ $image_size . '-height' ] = $src[2]; // } // // foreach( $image_sizes as $image_size ) // } } elseif(is_array( $data->cropped_image) || is_object($data->cropped_image)){ // Cropped image is not saved to media directory. Get data from original image instead $value = $this->getImageArray($data->original_image); // Get the relative url from data $relativeUrl = ''; if(is_array( $data->cropped_image)){ $relativeUrl = $data->cropped_image['image']; } else{ $relativeUrl = $data->cropped_image->image; } // Replace URL with cropped version $value['url'] = $this->getAbsoluteImageUrl($relativeUrl); // Calculate and replace sizes $imagePath = $this->getImagePath($relativeUrl); $dimensions = getimagesize($imagePath); $value['width'] = $dimensions[0]; $value['height'] = $dimensions[1]; // Add original image info $value['original_image'] = $this->getImageArray($data->original_image); } else{ //echo 'ELSE'; } } return $value; } function getImageArray($id){ $attachment = get_post( $id ); // validate if( !$attachment ) { return false; } // create array to hold value data $src = wp_get_attachment_image_src( $attachment->ID, 'full' ); $imageArray = array( 'id' => $attachment->ID, 'alt' => get_post_meta($attachment->ID, '_wp_attachment_image_alt', true), 'title' => $attachment->post_title, 'caption' => $attachment->post_excerpt, 'description' => $attachment->post_content, 'mime_type' => $attachment->post_mime_type, 'url' => $src[0], 'width' => $src[1], 'height' => $src[2], 'sizes' => array(), ); // find all image sizes $image_sizes = get_intermediate_image_sizes(); if( $image_sizes ) { foreach( $image_sizes as $image_size ) { // find src $src = wp_get_attachment_image_src( $attachment->ID, $image_size ); // add src $imageArray[ 'sizes' ][ $image_size ] = $src[0]; $imageArray[ 'sizes' ][ $image_size . '-width' ] = $src[1]; $imageArray[ 'sizes' ][ $image_size . '-height' ] = $src[2]; } } return $imageArray; } /* * input_admin_enqueue_scripts() * * This action is called in the admin_enqueue_scripts action on the edit screen where your field is created. * Use this action to add css + javascript to assist your create_field() action. * * $info http://codex.wordpress.org/Plugin_API/Action_Reference/admin_enqueue_scripts * @type action * @since 3.6 * @date 23/01/13 */ function input_admin_enqueue_scripts() { // register acf scripts wp_register_script('acf-input-image_crop', plugins_url('js/input-v4.js', __FILE__), array('acf-input', 'imgareaselect'), $this->settings['version']); wp_register_style('acf-input-image_crop', plugins_url('css/input.css', __FILE__), array('acf-input'), $this->settings['version']); wp_register_script( 'jcrop', includes_url( 'js/jcrop/jquery.Jcrop.min.css' )); // scripts wp_enqueue_script(array( 'acf-input-image_crop' )); wp_localize_script( 'acf-input-image_crop', 'ajax', array('nonce' => wp_create_nonce('acf_nonce')) ); // styles wp_enqueue_style(array( 'acf-input-image_crop', 'imgareaselect' )); } /* * field_group_admin_enqueue_scripts() * * This action is called in the admin_enqueue_scripts action on the edit screen where your field is edited. * Use this action to add css + javascript to assist your create_field_options() action. * * $info http://codex.wordpress.org/Plugin_API/Action_Reference/admin_enqueue_scripts * @type action * @since 3.6 * @date 23/01/13 */ function field_group_admin_enqueue_scripts() { // Note: This function can be removed if not used wp_register_script('acf-input-image-crop-options', $this->settings['dir'] . 'js/options-v4.js', array('jquery'), $this->settings['version']); wp_enqueue_script( 'acf-input-image-crop-options'); wp_register_style('acf-input-image-crop-options', $this->settings['dir'] . 'css/options.css'); wp_enqueue_style( 'acf-input-image-crop-options'); } /* * update_value() * * This filter is appied to the $value before it is updated in the db * * @type filter * @since 3.6 * @date 23/01/13 * * @param $value - the value which will be saved in the database * @param $post_id - the $post_id of which the value will be saved * @param $field - the field array holding all the field options * * @return $value - the modified value */ function update_value( $value, $post_id, $field ) { // array? if( is_array($value) && isset($value['id']) ) { $value = $value['id']; } // object? if( is_object($value) && isset($value->ID) ) { $value = $value->ID; } return $value; } function get_image_data($field){ $imageData = new stdClass(); $imageData->original_image = ''; $imageData->original_image_width = ''; $imageData->original_image_height = ''; $imageData->cropped_image = ''; $imageData->original_image_url = ''; $imageData->preview_image_url = ''; $imageData->image_url = ''; if($field['value'] == ''){ // Field has not yet been saved or is an empty image field return $imageData; } $data = json_decode($field['value']); if(! is_object($data)){ // Field was saved as a regular image field $imageAtts = wp_get_attachment_image_src($field['value'], 'full'); $imageData->original_image = $field['value']; $imageData->original_image_width = $imageAtts[1]; $imageData->original_image_height = $imageAtts[2]; $imageData->preview_image_url = $this->get_image_src($field['value'], $field['preview_size']); $imageData->image_url = $this->get_image_src($field['value'], 'full'); $imageData->original_image_url = $imageData->image_url; return $imageData; } if( !is_numeric($data->original_image) ) { // The field has been saved, but has no image return $imageData; } // By now, we have at least a saved original image $imageAtts = wp_get_attachment_image_src($data->original_image, 'full'); $imageData->original_image = $data->original_image; $imageData->original_image_width = $imageAtts[1]; $imageData->original_image_height = $imageAtts[2]; $imageData->original_image_url = $this->get_image_src($data->original_image, 'full'); // Set defaults to original image $imageData->image_url = $this->get_image_src($data->original_image, 'full'); $imageData->preview_image_url = $this->get_image_src($data->original_image, $field['preview_size']); // Check if there is a cropped version and set appropriate attributes if(is_numeric($data->cropped_image)){ // Cropped image was saved to media library ans has an ID $imageData->cropped_image = $data->cropped_image; $imageData->image_url = $this->get_image_src($data->cropped_image, 'full'); $imageData->preview_image_url = $this->get_image_src($data->cropped_image, $field['preview_size']); } elseif(is_object($data->cropped_image)){ // Cropped image was not saved to media library and is only stored by its URL $imageData->cropped_image = $data->cropped_image; // Generate appropriate URLs $mediaDir = wp_upload_dir(); $imageData->image_url = $mediaDir['baseurl'] . '/' . $data->cropped_image->image; $imageData->preview_image_url = $mediaDir['baseurl'] . '/' . $data->cropped_image->preview; } return $imageData; } function perform_crop(){ $targetWidth = $_POST['target_width']; $targetHeight = $_POST['target_height']; $saveToMediaLibrary = $_POST['save_to_media_library'] == 'yes'; $imageData = $this->generate_cropped_image($_POST['id'], $_POST['x1'], $_POST['x2'], $_POST['y1'], $_POST['y2'], $targetWidth, $targetHeight, $saveToMediaLibrary, $_POST['preview_size']); // $previewUrl = wp_get_attachment_image_src( $id, $_POST['preview_size']); // $fullUrl = wp_get_attachment_image_src( $id, 'full'); echo json_encode($imageData); die(); } function generate_cropped_image($id, $x1, $x2, $y1, $y2, $targetW, $targetH, $saveToMediaLibrary, $previewSize){//$id, $x1, $x2, $y$, $y2, $targetW, $targetH){ require_once ABSPATH . "/wp-admin/includes/file.php"; require_once ABSPATH . "/wp-admin/includes/image.php"; // Create the variable that will hold the new image data $imageData = array(); // Fetch media library info $mediaDir = wp_upload_dir(); // Get original image info $originalImageData = wp_get_attachment_metadata($id); // Get image editor from original image path to crop the image $image = wp_get_image_editor( $mediaDir['basedir'] . '/' . $originalImageData['file'] ); if(! is_wp_error( $image ) ){ // Crop the image using the provided measurements $image->crop($x1, $y1, $x2 - $x1, $y2 - $y1, $targetW, $targetH); // Retrieve original filename and seperate it from its file extension $originalFileName = explode('.', basename($originalImageData['file'])); // Retrieve and remove file extension from array $originalFileExtension = array_pop($originalFileName); // Generate new base filename $targetFileName = implode('.', $originalFileName) . '_' . $targetW . 'x' . $targetH . apply_filters('acf-image-crop/filename_postfix', '_acf_cropped') . '.' . $originalFileExtension; // Generate target path new file using existing media library $targetFilePath = $mediaDir['path'] . '/' . wp_unique_filename( $mediaDir['path'], $targetFileName); // Get the relative path to save as the actual image url $targetRelativePath = str_replace($mediaDir['basedir'] . '/', '', $targetFilePath); // Save the image to the target path if(is_wp_error($image->save($targetFilePath))){ // There was an error saving the image //TODO handle it } // If file should be saved to media library, create an attachment for it at get the new attachment ID if($saveToMediaLibrary){ // Generate attachment from created file // Get the filetype $wp_filetype = wp_check_filetype(basename($targetFilePath), null ); $attachment = array( 'guid' => $mediaDir['url'] . '/' . basename($targetFilePath), 'post_mime_type' => $wp_filetype['type'], 'post_title' => preg_replace('/\.[^.]+$/', '', basename($targetFilePath)), 'post_content' => '', 'post_status' => 'inherit' ); $attachmentId = wp_insert_attachment( $attachment, $targetFilePath); $attachmentData = wp_generate_attachment_metadata( $attachmentId, $targetFilePath ); wp_update_attachment_metadata( $attachmentId, $attachmentData ); add_post_meta($attachmentId, 'acf_is_cropped', 'true', true); // Add the id to the imageData-array $imageData['value'] = $attachmentId; // Add the image url $imageUrlObject = wp_get_attachment_image_src( $attachmentId, 'full'); $imageData['url'] = $imageUrlObject[0]; // Add the preview url as well $previewUrlObject = wp_get_attachment_image_src( $attachmentId, $previewSize); $imageData['preview_url'] = $previewUrlObject[0]; } // Else we need to return the actual path of the cropped image else{ // Add the image url to the imageData-array $imageData['value'] = array('image' => $targetRelativePath); $imageData['url'] = $mediaDir['baseurl'] . '/' . $targetRelativePath; // Get preview size dimensions global $_wp_additional_image_sizes; $previewWidth = 0; $previewHeight = 0; $crop = 0; if (isset($_wp_additional_image_sizes[$previewSize])) { $previewWidth = intval($_wp_additional_image_sizes[$previewSize]['width']); $previewHeight = intval($_wp_additional_image_sizes[$previewSize]['height']); $crop = $_wp_additional_image_sizes[$previewSize]['crop']; } else { $previewWidth = get_option($previewSize.'_size_w'); $previewHeight = get_option($previewSize.'_size_h'); $crop = get_option($previewSize.'_crop'); } // Generate preview file path $previewFilePath = $mediaDir['path'] . '/' . wp_unique_filename( $mediaDir['path'], 'preview_' . $targetFileName); $previewRelativePath = str_replace($mediaDir['basedir'] . '/', '', $previewFilePath); // Get image editor from cropped image $croppedImage = wp_get_image_editor( $targetFilePath ); $croppedImage->resize($previewWidth, $previewHeight, $crop); // Save the preview $croppedImage->save($previewFilePath); // Add the preview url $imageData['preview_url'] = $mediaDir['baseurl'] . '/' . $previewRelativePath; $imageData['value']['preview'] = $previewRelativePath; } $imageData['success'] = true; return $imageData; } else{ // Handle WP_ERROR $response = array(); $response['success'] = false; $response['error_message'] = ''; foreach($image->error_data as $code => $message){ $response['error_message'] .= '

' . $code . ': ' . $message . '

'; } return $response; } } function get_image_src($id, $size = 'thumbnail'){ $atts = wp_get_attachment_image_src( $id, $size); return $atts[0]; } function getAbsoluteImageUrl($relativeUrl){ $mediaDir = wp_upload_dir(); return $mediaDir['baseurl'] . '/' . $relativeUrl; } function getImagePath($relativePath){ $mediaDir = wp_upload_dir(); return $mediaDir['basedir'] . '/' . $relativePath; } function filterMediaQuery($args){ // get options $options = get_option( 'acf_image_crop_settings' ); $hide = isset($options['hide_cropped']) && $options['hide_cropped']; // If hide option is enabled, do not select items with the acf_is_cropped meta-field if($hide){ $args['meta_query']= array( array( 'key' => 'acf_is_cropped', 'compare' => 'NOT EXISTS' ) ); } return $args; } function registerSettings(){ add_settings_section( 'acf_image_crop_settings', __('ACF Image Crop Settings','acf-image_crop'), array($this, 'displayImageCropSettingsSection'), 'media' ); register_setting( 'media', // settings page 'acf_image_crop_settings' // option name ); add_settings_field( 'acf_image_crop_hide_cropped', // id __('Hide cropped images from media dialog', 'acf-image_crop'), // setting title array($this, 'displayHideFromMediaInput'), // display callback 'media', // settings page 'acf_image_crop_settings' // settings section ); add_settings_field( 'acf_image_crop_retina_mode', // id __('Enable global retina mode (beta)', 'acf-image_crop'), // setting title array($this, 'displayRetinaModeInput'), // display callback 'media', // settings page 'acf_image_crop_settings' // settings section ); } function displayHideFromMediaInput(){ // Get plugin options $options = get_option( 'acf_image_crop_settings' ); $hide = isset($options['hide_cropped']) && $options['hide_cropped']; // echo the field ?> value='true' /> value='true' /> options[$key]) ? $this->options[$key] : null; } } // create field new acf_field_image_crop(); ?>