Advanced Custom Fields: Image Crop Add-on - Version 1.0

Version Description

  • Added option to save the image to media library or refer directly to the created image, not using the media library.
  • Added better compatibility with the native image field making it possible to migrate from the regular image field to the crop-image field without losing the images currently attached. (It doesn't work the other way around)
Download this release

Release Info

Developer andersthorborg
Plugin Icon wp plugin Advanced Custom Fields: Image Crop Add-on
Version 1.0
Comparing to
See all releases

Code changes from version 0.8 to 1.0

README.md ADDED
@@ -0,0 +1,70 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ -----------------------
2
+
3
+ # ACF { Image Crop Add-on
4
+
5
+ Adds an 'Image with user-crop' field type for the [Advanced Custom Fields](http://wordpress.org/extend/plugins/advanced-custom-fields/) WordPress plugin.
6
+
7
+
8
+
9
+ -----------------------
10
+
11
+ ### Overview
12
+ ACF image crop is an extended version of the native Image-field in ACF.
13
+ The field gives the developer/administrator the option to predefine a size for the image, which the user is prompted to crop on the various edit screens. This solves the common issue of images being cropped inappropriately by the automated center-crop, that wordpress performs.
14
+
15
+ The plugin supports the defined image sizes as well as a custom option, enabling the developer to specify the dimensions from within the field edit screen.
16
+
17
+ The field can be configured to enforce a hard crop or a minimal-dimension-based crop. The hard crop will lock the aspect ratio of the crop where as the minimal-dimension-based crop will not allow the user to crop the image below the specified dimensions.
18
+
19
+ This plugin diverts from plugins like [Manual Image Crop](http://wordpress.org/plugins/manual-image-crop/) in that when the user crops an image, a new attachment is generated, so that the relevant crop only applies in the context it is edited. It also spares the user from dealing with the concept of various image sizes.
20
+
21
+ As of version 1.0 the field can be configured to either create the cropped image as a media-item (the default behavior) or simply create it and refer directly to the file without adding it to the media library. This will prevent the media library from being cluttered with several cropped versions of the same image. When this option is selected the only available return type for the field is URL.
22
+
23
+
24
+
25
+ ### Compatibility
26
+
27
+ This add-on will work with:
28
+
29
+ * version 4 and up
30
+
31
+ ### Installation
32
+
33
+ This add-on can be treated as both a WP plugin and a theme include.
34
+
35
+ **Install as Plugin**
36
+
37
+ 1. Copy the 'acf-image_crop' folder into your plugins folder
38
+ 2. Activate the plugin via the Plugins admin page
39
+
40
+ **Include within theme**
41
+
42
+ 1. Copy the 'acf-image_crop' folder into your theme folder (can use sub folders). You can place the folder anywhere inside the 'wp-content' directory
43
+ 2. Edit your functions.php file and add the code below (Make sure the path is correct to include the acf-image_crop.php file)
44
+
45
+ ```php
46
+ include_once('acf-image_crop/acf-image_crop.php');
47
+ ```
48
+
49
+ ### Setup
50
+
51
+ The field has the same options as the standard image field. Besides from these, other options are available:
52
+
53
+ **Crop type**
54
+
55
+ Defines what kind of crop the user is going to perform. If the *Hard crop* option is selected, the aspect ratio of the selected size will be maintained.
56
+
57
+ The *Minimal dimensions* option uses the specified size as minima requirements. With this option, the user will not be able to crop the image below the specified dimensions. If a dimension is not specified, the user will be able to crop that dimension to any value.
58
+
59
+ **Target size**
60
+
61
+ The size, that the image will ultimately be cropped into. This list includes the currently defined image sizes as well as the custom option. If the custom option is selected, a *dimension* opition will become available asking for width:height. NB. When using a custom target size along with the hard crop option **both width and height must be specified**.
62
+
63
+ **Force crop**
64
+
65
+ On the image field will be a *Crop*-button triggering the crop dialog. If the force crop option is enabled, the crop dialog will show up as soon as the user selects an image.
66
+
67
+ ###Further Details
68
+ Whenever the user selects an image, the original image is stored, so whenever the user decides to re-crop the image the user will see the image first selected, and not the cropped one. Every crop made creates a seperate media item, which allows for multiple uses of the same image wihtout overwriting previous crops.
69
+
70
+ The plugin has only been tested with Chrome for Mac, and still needs a lot of work. Please let me know of any issues or feature requests.
acf-image-crop.php CHANGED
@@ -3,7 +3,7 @@
3
  Plugin Name: Advanced Custom Fields: Image Crop Add-on
4
  Plugin URI: https://github.com/andersthorborg/ACF-Image-Crop
5
  Description: An image field making it possible/required for the user to crop the selected image to the specified image size or dimensions
6
- Version: 0.8
7
  Author: Anders Thorborg
8
  Author URI: http://thorb.org
9
  License: GPLv2 or later
3
  Plugin Name: Advanced Custom Fields: Image Crop Add-on
4
  Plugin URI: https://github.com/andersthorborg/ACF-Image-Crop
5
  Description: An image field making it possible/required for the user to crop the selected image to the specified image size or dimensions
6
+ Version: 1.0
7
  Author: Anders Thorborg
8
  Author URI: http://thorb.org
9
  License: GPLv2 or later
css/options.css CHANGED
@@ -3,4 +3,7 @@
3
  }
4
  .field_type-image_crop .dimensions-wrap .dimension{
5
  width:150px;
 
 
 
6
  }
3
  }
4
  .field_type-image_crop .dimensions-wrap .dimension{
5
  width:150px;
6
+ }
7
+ .field_type-image_crop label.disabled{
8
+ color: #ccc;
9
  }
image_crop-v4.php CHANGED
@@ -26,7 +26,8 @@ class acf_field_image_crop extends acf_field_image
26
  'force_crop' => 'no',
27
  'crop_type' => 'hard',
28
  'preview_size' => 'medium',
29
- 'save_format' => 'object',
 
30
  'target_size' => 'thumbnail'
31
  // add default here to merge into your field.
32
  // This makes life easy when creating the field options as you don't need to use any if( isset('') ) logic. eg:
@@ -44,15 +45,15 @@ class acf_field_image_crop extends acf_field_image
44
  );
45
 
46
  // add ajax action to be able to retrieve full image size via javascript
47
- add_action( 'wp_ajax_acf_image_crop_get_image_size', array( &$this, 'crop_get_image_size' ) );
48
  add_action( 'wp_ajax_acf_image_crop_perform_crop', array( &$this, 'perform_crop' ) );
49
 
50
  }
51
 
52
  // AJAX handler for retieving full image dimensions from ID
53
  public function crop_get_image_size()
54
- {
55
- $img = wp_get_attachment_image_src( $_POST['image_id'], 'full');
56
  if($img){
57
  echo json_encode( array(
58
  'url' => $img[0],
@@ -90,27 +91,6 @@ class acf_field_image_crop extends acf_field_image
90
 
91
  // Create Field Options HTML
92
  ?>
93
- <tr class="field_option field_option_<?php echo $this->name; ?>">
94
- <td class="label">
95
- <label><?php _e("Return Value",'acf'); ?></label>
96
- <p><?php _e("Specify the returned value on front end",'acf') ?></p>
97
- </td>
98
- <td>
99
- <?php
100
- do_action('acf/create_field', array(
101
- 'type' => 'radio',
102
- 'name' => 'fields['.$key.'][save_format]',
103
- 'value' => $field['save_format'],
104
- 'layout' => 'horizontal',
105
- 'choices' => array(
106
- 'object' => __("Image Object",'acf'),
107
- 'url' => __("Image URL",'acf'),
108
- 'id' => __("Image ID",'acf')
109
- )
110
- ));
111
- ?>
112
- </td>
113
- </tr>
114
  <tr class="field_option field_option_<?php echo $this->name; ?>">
115
  <td class="label">
116
  <label><?php _e("Crop type", 'acf'); ?></label>
@@ -122,11 +102,11 @@ class acf_field_image_crop extends acf_field_image
122
  do_action('acf/create_field', array(
123
  'type' => 'select',
124
  'name' => 'fields[' . $key . '][crop_type]',
125
- 'value' => $field['target_size'],
126
  'class' => 'crop-type-select',
127
  'choices' => array(
128
  'hard' => __('Hard crop', 'acf'),
129
- 'min' => __('Minimal dimensions', 'acf')
130
  )
131
  ));
132
 
@@ -181,6 +161,24 @@ class acf_field_image_crop extends acf_field_image
181
  ?>
182
  </td>
183
  </tr>
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
184
  <tr class="field_option field_option_<?php echo $this->name; ?>">
185
  <td class="label">
186
  <label><?php _e("Force crop",'acf'); ?></label>
@@ -203,23 +201,47 @@ class acf_field_image_crop extends acf_field_image
203
  </tr>
204
  <tr class="field_option field_option_<?php echo $this->name; ?>">
205
  <td class="label">
206
- <label><?php _e("Preview Size", 'acf'); ?></label>
207
- <p class="description"><?php _e("Select the target size for this field", 'acf'); ?></p>
208
  </td>
209
  <td>
210
  <?php
211
  do_action('acf/create_field', array(
212
- 'type' => 'select',
213
- 'name' => 'fields[' . $key . '][preview_size]',
214
- 'value' => $field['preview_size'],
215
- 'class' => 'preview-size-select',
216
- 'choices' => apply_filters('acf/get_image_sizes', array())
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
217
  ));
218
-
219
  ?>
220
  </td>
221
  </tr>
222
-
223
  <?php
224
 
225
  }
@@ -243,25 +265,42 @@ class acf_field_image_crop extends acf_field_image
243
  /*
244
  $field = array_merge($this->defaults, $field);
245
  */
246
-
247
- // perhaps use $field['preview_size'] to alter the markup?
248
  $data = json_decode($field['value']);
249
  // create Field HTML
250
  // vars
251
  $o = array(
252
  'class' => '',
253
  'url' => '',
254
- );
255
- $originalImage = null;
256
- if( $data && is_object($data) && is_numeric($data->original_image) )
257
- {
258
- $originalImage = wp_get_attachment_image_src($data->original_image, 'full');
259
- $url = wp_get_attachment_image_src($data->original_image, $field['preview_size']);
260
- if(is_numeric($data->cropped_image)){
261
- $url = wp_get_attachment_image_src($data->cropped_image, $field['preview_size']);
262
- }
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
263
  $o['class'] = 'active';
264
- $o['url'] = $url[0];
265
  }
266
  $width = 0;
267
  $height = 0;
@@ -271,24 +310,24 @@ class acf_field_image_crop extends acf_field_image
271
  }
272
  else{
273
  global $_wp_additional_image_sizes;
274
- $s = $field['target_size'];
275
  if (isset($_wp_additional_image_sizes[$s])) {
276
  $width = intval($_wp_additional_image_sizes[$s]['width']);
277
  $height = intval($_wp_additional_image_sizes[$s]['height']);
278
  } else {
279
  $width = get_option($s.'_size_w');
280
  $height = get_option($s.'_size_h');
281
- }
282
- }
283
  ?>
284
- <div class="acf-image-uploader clearfix <?php echo $o['class']; ?>" data-preview_size="<?php echo $field['preview_size']; ?>" data-library="<?php echo $field['library']; ?>" data-width="<?php echo $width ?>" data-height="<?php echo $height ?>" data-crop-type="<?php echo $field['crop_type'] ?>" <?php echo ($field['force_crop'] == 'yes' ? 'data-force-crop="true"' : '') ?> >
285
- <input class="acf-image-value" data-original-image="<?php echo $data->original_image ?>" data-cropped-image="<?php echo $data->cropped_image ?>" type="hidden" name="<?php echo $field['name']; ?>" value="<?php echo htmlspecialchars($field['value']); ?>" />
286
- <div class="has-image">
287
  <div class="image-section">
288
  <div class="hover">
289
  <ul class="bl">
290
  <li><a class="acf-button-delete ir" href="#"><?php _e("Remove",'acf'); ?></a></li>
291
- <li><a class="acf-button-edit ir" href="#"><?php _e("Edit",'acf'); ?></a></li>
292
  </ul>
293
  </div>
294
  <img class="acf-image-image" src="<?php echo $o['url']; ?>" alt=""/>
@@ -297,8 +336,8 @@ class acf_field_image_crop extends acf_field_image
297
  <div class="crop-stage">
298
  <div class="crop-action">
299
  <h4>Crop the image</h4>
300
- <?php if ($originalImage ): ?>
301
- <img class="crop-image" src="<?php echo $originalImage[0] ?>" data-width="<?php echo $originalImage[1] ?>" data-height="<?php echo $originalImage[2] ?>" alt="">
302
  <?php endif ?>
303
  </div>
304
  <div class="crop-preview">
@@ -310,7 +349,7 @@ class acf_field_image_crop extends acf_field_image
310
  </div>
311
  <!-- <img src="<?php echo $o['url']; ?>" alt=""/> -->
312
  </div>
313
- <a href="#" class="button button-large init-crop-button">Crop</a>
314
  </div>
315
  </div>
316
  <div class="no-image">
@@ -320,7 +359,7 @@ class acf_field_image_crop extends acf_field_image
320
  <?php
321
  }
322
 
323
-
324
 
325
  /*
326
  * format_value_for_api()
@@ -337,10 +376,9 @@ class acf_field_image_crop extends acf_field_image
337
  *
338
  * @return $value - the modified value
339
  */
340
-
341
  function format_value_for_api( $value, $post_id, $field )
342
  {
343
-
344
  // validate
345
  if( !$value )
346
  {
@@ -352,63 +390,81 @@ class acf_field_image_crop extends acf_field_image
352
  }
353
 
354
  $value = $data->cropped_image;
355
-
356
  // format
357
  if( $field['save_format'] == 'url' )
358
  {
359
- $value = wp_get_attachment_url( $data->cropped_image );
 
 
 
 
 
 
 
 
 
 
360
  }
361
  elseif( $field['save_format'] == 'object' )
362
  {
363
- $attachment = get_post( $data->cropped_image );
364
-
365
-
366
- // validate
367
- if( !$attachment )
368
- {
369
- return false;
370
- }
371
-
372
-
373
- // create array to hold value data
374
- $src = wp_get_attachment_image_src( $attachment->ID, 'full' );
375
-
376
- $value = array(
377
- 'id' => $attachment->ID,
378
- 'alt' => get_post_meta($attachment->ID, '_wp_attachment_image_alt', true),
379
- 'title' => $attachment->post_title,
380
- 'caption' => $attachment->post_excerpt,
381
- 'description' => $attachment->post_content,
382
- 'mime_type' => $attachment->post_mime_type,
383
- 'url' => $src[0],
384
- 'width' => $src[1],
385
- 'height' => $src[2],
386
- 'sizes' => array(),
387
- );
388
-
389
-
390
- // find all image sizes
391
- $image_sizes = get_intermediate_image_sizes();
392
-
393
- if( $image_sizes )
394
- {
395
- foreach( $image_sizes as $image_size )
396
  {
397
- // find src
398
- $src = wp_get_attachment_image_src( $attachment->ID, $image_size );
399
-
400
- // add src
401
- $value[ 'sizes' ][ $image_size ] = $src[0];
402
- $value[ 'sizes' ][ $image_size . '-width' ] = $src[1];
403
- $value[ 'sizes' ][ $image_size . '-height' ] = $src[2];
 
 
 
 
404
  }
405
- // foreach( $image_sizes as $image_size )
406
  }
407
- // if( $image_sizes )
408
-
409
- }
 
 
 
 
 
 
 
 
410
  return $value;
411
-
412
  }
413
 
414
  /*
@@ -428,15 +484,15 @@ class acf_field_image_crop extends acf_field_image
428
  // Note: This function can be removed if not used
429
 
430
 
431
- // register acf scripts
432
  wp_register_script('acf-input-image_crop', $this->settings['dir'] . 'js/input.js', array('acf-input', 'imgareaselect'), $this->settings['version']);
433
 
434
- wp_register_style('acf-input-image_crop', $this->settings['dir'] . 'css/input.css', array('acf-input'), $this->settings['version']);
435
  wp_register_script( 'jcrop', includes_url( 'js/jcrop/jquery.Jcrop.min.css' ));
436
 
437
 
438
- // scripts
439
- wp_enqueue_script(array(
440
  'acf-input-image_crop'
441
  ));
442
 
@@ -473,7 +529,7 @@ class acf_field_image_crop extends acf_field_image
473
  wp_enqueue_style( 'acf-input-image-crop-options');
474
  }
475
 
476
-
477
  /*
478
  * update_value()
479
  *
@@ -489,59 +545,211 @@ class acf_field_image_crop extends acf_field_image
489
  *
490
  * @return $value - the modified value
491
  */
492
-
493
  function update_value( $value, $post_id, $field )
494
  {
495
  // array?
496
  if( is_array($value) && isset($value['id']) )
497
  {
498
- $value = $value['id'];
499
  }
500
-
501
  // object?
502
  if( is_object($value) && isset($value->ID) )
503
  {
504
  $value = $value->ID;
505
  }
506
-
507
  return $value;
508
  }
509
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
510
  function perform_crop(){
511
  $targetWidth = $_POST['target_width'];
512
- $targetHeight = $_POST['target_height'];
513
- $id = $this->generate_cropped_image($_POST['id'], $_POST['x1'], $_POST['x2'], $_POST['y1'], $_POST['y2'], $targetWidth, $targetHeight);
514
- $previewUrl = wp_get_attachment_image_src( $id, $_POST['preview_size']);
515
- $fullUrl = wp_get_attachment_image_src( $id, 'full');
516
- $data = array(
517
- 'id' => $id,
518
- 'url_preview' => $previewUrl[0],
519
- 'url_full' => $fullUrl[0]
520
- );
521
- echo json_encode($data);
522
  die();
523
  }
524
 
525
- function generate_cropped_image($id, $x1, $x2, $y1, $y2, $targetW, $targetH){//$id, $x1, $x2, $y$, $y2, $targetW, $targetH){
526
  require_once ABSPATH . "/wp-admin/includes/file.php";
527
- require_once ABSPATH . "/wp-admin/includes/image.php";
528
-
529
- $filename = wp_crop_image($id, $x1, $y1, $x2 - $x1, $y2 - $y1, $targetW, $targetH);
530
- // GENERATE NEW ATTACHMENT FROM NEW FILE
531
- $wp_filetype = wp_check_filetype(basename($filename), null );
532
- $attachment = array(
533
- 'guid' => $filename,
534
- 'post_mime_type' => $wp_filetype['type'],
535
- 'post_title' => preg_replace('/\.[^.]+$/', '', basename($filename)),
536
- 'post_content' => '',
537
- 'post_status' => 'inherit'
538
- );
539
- $attach_id = wp_insert_attachment( $attachment, $filename);
540
- $attach_data = wp_generate_attachment_metadata( $attach_id, $filename );
541
- wp_update_attachment_metadata( $attach_id, $attach_data );
542
-
543
- // Return ID of cropped image
544
- return $attach_id;
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
545
  }
546
 
547
  }
26
  'force_crop' => 'no',
27
  'crop_type' => 'hard',
28
  'preview_size' => 'medium',
29
+ 'save_format' => 'id',
30
+ 'save_in_media_library' => 'yes',
31
  'target_size' => 'thumbnail'
32
  // add default here to merge into your field.
33
  // This makes life easy when creating the field options as you don't need to use any if( isset('') ) logic. eg:
45
  );
46
 
47
  // add ajax action to be able to retrieve full image size via javascript
48
+ add_action( 'wp_ajax_acf_image_crop_get_image_size', array( &$this, 'crop_get_image_size' ) );
49
  add_action( 'wp_ajax_acf_image_crop_perform_crop', array( &$this, 'perform_crop' ) );
50
 
51
  }
52
 
53
  // AJAX handler for retieving full image dimensions from ID
54
  public function crop_get_image_size()
55
+ {
56
+ $img = wp_get_attachment_image_src( $_POST['image_id'], 'full');
57
  if($img){
58
  echo json_encode( array(
59
  'url' => $img[0],
91
 
92
  // Create Field Options HTML
93
  ?>
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
94
  <tr class="field_option field_option_<?php echo $this->name; ?>">
95
  <td class="label">
96
  <label><?php _e("Crop type", 'acf'); ?></label>
102
  do_action('acf/create_field', array(
103
  'type' => 'select',
104
  'name' => 'fields[' . $key . '][crop_type]',
105
+ 'value' => $field['crop_type'],
106
  'class' => 'crop-type-select',
107
  'choices' => array(
108
  'hard' => __('Hard crop', 'acf'),
109
+ 'min' => __('Minimal dimensions', 'acf')
110
  )
111
  ));
112
 
161
  ?>
162
  </td>
163
  </tr>
164
+ <tr class="field_option field_option_<?php echo $this->name; ?>">
165
+ <td class="label">
166
+ <label><?php _e("Preview Size", 'acf'); ?></label>
167
+ <p class="description"><?php _e("Select the target size for this field", 'acf'); ?></p>
168
+ </td>
169
+ <td>
170
+ <?php
171
+ do_action('acf/create_field', array(
172
+ 'type' => 'select',
173
+ 'name' => 'fields[' . $key . '][preview_size]',
174
+ 'value' => $field['preview_size'],
175
+ 'class' => 'preview-size-select',
176
+ 'choices' => apply_filters('acf/get_image_sizes', array())
177
+ ));
178
+
179
+ ?>
180
+ </td>
181
+ </tr>
182
  <tr class="field_option field_option_<?php echo $this->name; ?>">
183
  <td class="label">
184
  <label><?php _e("Force crop",'acf'); ?></label>
201
  </tr>
202
  <tr class="field_option field_option_<?php echo $this->name; ?>">
203
  <td class="label">
204
+ <label><?php _e("Save cropped image in media library",'acf'); ?></label>
205
+ <p><?php _e("If the cropped image is not saved in the media library, the \"Image URL\" is the only available return value.", 'acf') ?></p>
206
  </td>
207
  <td>
208
  <?php
209
  do_action('acf/create_field', array(
210
+ 'type' => 'radio',
211
+ 'name' => 'fields['.$key.'][save_in_media_library]',
212
+ 'value' => $field['save_in_media_library'],
213
+ 'layout' => 'horizontal',
214
+ 'choices' => array(
215
+ 'yes' => __("Yes",'acf'),
216
+ 'no' => __("No",'acf')
217
+ ),
218
+ 'class' => 'save-in-media-library-select'
219
+ ));
220
+ ?>
221
+ </td>
222
+ </tr>
223
+ <tr class="field_option field_option_<?php echo $this->name; ?>">
224
+ <td class="label">
225
+ <label><?php _e("Return Value",'acf'); ?></label>
226
+ <p><?php _e("Specify the returned value on front end",'acf') ?></p>
227
+ </td>
228
+ <td>
229
+ <?php
230
+ do_action('acf/create_field', array(
231
+ 'type' => 'radio',
232
+ 'name' => 'fields['.$key.'][save_format]',
233
+ 'value' => $field['save_format'],
234
+ 'layout' => 'horizontal',
235
+ 'choices' => array(
236
+ 'url' => __("Image URL",'acf'),
237
+ 'id' => __("Image ID",'acf'),
238
+ 'object' => __("Image Object",'acf')
239
+ ),
240
+ 'class' => 'return-value-select'
241
  ));
 
242
  ?>
243
  </td>
244
  </tr>
 
245
  <?php
246
 
247
  }
265
  /*
266
  $field = array_merge($this->defaults, $field);
267
  */
268
+ // perhaps use $field['preview_size'] to alter the markup?
 
269
  $data = json_decode($field['value']);
270
  // create Field HTML
271
  // vars
272
  $o = array(
273
  'class' => '',
274
  'url' => '',
275
+ );
276
+ // $originalImage = null;
277
+ // if( $data && is_object($data) && is_numeric($data->original_image) )
278
+ // {
279
+ // $originalImage = wp_get_attachment_image_src($data->original_image, 'full');
280
+ // $url = wp_get_attachment_image_src($data->original_image, $field['preview_size']);
281
+ // if($field['save_in_media_library'] == 'yes'){
282
+ // if(is_numeric($data->cropped_image)){
283
+ // $url = wp_get_attachment_image_src($data->cropped_image, $field['preview_size']);
284
+ // }
285
+ // }
286
+ // else{
287
+ // if($data->cropped_image_url){
288
+ // $url = $data->cropped_image_url;
289
+ // }
290
+ // else{
291
+
292
+ // }
293
+ // }
294
+
295
+ // $o['class'] = 'active';
296
+ // $o['url'] = $url[0];
297
+ // }
298
+ $imageData = $this->get_image_data($field);
299
+ //print_r($field);
300
+ $originalImage = wp_get_attachment_image_src($imageData->original_image, 'full');
301
+ if($imageData->original_image){
302
  $o['class'] = 'active';
303
+ $o['url'] = $imageData->preview_image_url;
304
  }
305
  $width = 0;
306
  $height = 0;
310
  }
311
  else{
312
  global $_wp_additional_image_sizes;
313
+ $s = $field['target_size'];
314
  if (isset($_wp_additional_image_sizes[$s])) {
315
  $width = intval($_wp_additional_image_sizes[$s]['width']);
316
  $height = intval($_wp_additional_image_sizes[$s]['height']);
317
  } else {
318
  $width = get_option($s.'_size_w');
319
  $height = get_option($s.'_size_h');
320
+ }
321
+ }
322
  ?>
323
+ <div class="acf-image-uploader clearfix <?php echo $o['class']; ?>" data-field-id="<?php echo $field['key'] ?>" data-preview_size="<?php echo $field['preview_size']; ?>" data-library="<?php echo $field['library']; ?>" data-width="<?php echo $width ?>" data-height="<?php echo $height ?>" data-crop-type="<?php echo $field['crop_type'] ?>" <?php echo ($field['force_crop'] == 'yes' ? 'data-force-crop="true"' : '')?> data-save-to-media-library="<?php echo $field['save_in_media_library'] ?>" >
324
+ <input class="acf-image-value" data-original-image="<?php echo $data->original_image ?>" data-cropped-image="<?php echo json_encode($data->cropped_image) ?>" type="hidden" name="<?php echo $field['name']; ?>" value="<?php echo htmlspecialchars($field['value']); ?>" />
325
+ <div class="has-image">
326
  <div class="image-section">
327
  <div class="hover">
328
  <ul class="bl">
329
  <li><a class="acf-button-delete ir" href="#"><?php _e("Remove",'acf'); ?></a></li>
330
+ <li><a class="acf-button-edit ir" href="#"><?php _e("Edit",'acf'); ?></a></li>
331
  </ul>
332
  </div>
333
  <img class="acf-image-image" src="<?php echo $o['url']; ?>" alt=""/>
336
  <div class="crop-stage">
337
  <div class="crop-action">
338
  <h4>Crop the image</h4>
339
+ <?php if ($imageData->original_image ): ?>
340
+ <img class="crop-image" src="<?php echo $imageData->original_image_url ?>" data-width="<?php echo $imageData->original_image_width ?>" data-height="<?php echo $imageData->original_image_height ?>" alt="">
341
  <?php endif ?>
342
  </div>
343
  <div class="crop-preview">
349
  </div>
350
  <!-- <img src="<?php echo $o['url']; ?>" alt=""/> -->
351
  </div>
352
+ <a href="#" class="button button-large init-crop-button">Crop</a>
353
  </div>
354
  </div>
355
  <div class="no-image">
359
  <?php
360
  }
361
 
362
+
363
 
364
  /*
365
  * format_value_for_api()
376
  *
377
  * @return $value - the modified value
378
  */
379
+
380
  function format_value_for_api( $value, $post_id, $field )
381
  {
 
382
  // validate
383
  if( !$value )
384
  {
390
  }
391
 
392
  $value = $data->cropped_image;
393
+
394
  // format
395
  if( $field['save_format'] == 'url' )
396
  {
397
+ if(is_numeric($data->cropped_image)){
398
+ $value = wp_get_attachment_url( $data->cropped_image );
399
+ }
400
+ elseif(is_array($data->cropped_image)){
401
+
402
+ $value = $this->getAbsoluteImageUrl($data->cropped_image['image']);
403
+ }
404
+ elseif(is_object($data->cropped_image)){
405
+ $value = $this->getAbsoluteImageUrl($data->cropped_image->image);
406
+ }
407
+
408
  }
409
  elseif( $field['save_format'] == 'object' )
410
  {
411
+ if(is_numeric($data->cropped_image )){
412
+ $attachment = get_post( $data->cropped_image );
413
+ // validate
414
+ if( !$attachment )
415
+ {
416
+ return false;
417
+ }
418
+
419
+
420
+ // create array to hold value data
421
+ $src = wp_get_attachment_image_src( $attachment->ID, 'full' );
422
+
423
+ $value = array(
424
+ 'id' => $attachment->ID,
425
+ 'alt' => get_post_meta($attachment->ID, '_wp_attachment_image_alt', true),
426
+ 'title' => $attachment->post_title,
427
+ 'caption' => $attachment->post_excerpt,
428
+ 'description' => $attachment->post_content,
429
+ 'mime_type' => $attachment->post_mime_type,
430
+ 'url' => $src[0],
431
+ 'width' => $src[1],
432
+ 'height' => $src[2],
433
+ 'sizes' => array(),
434
+ );
435
+
436
+
437
+ // find all image sizes
438
+ $image_sizes = get_intermediate_image_sizes();
439
+
440
+ if( $image_sizes )
 
 
 
441
  {
442
+ foreach( $image_sizes as $image_size )
443
+ {
444
+ // find src
445
+ $src = wp_get_attachment_image_src( $attachment->ID, $image_size );
446
+
447
+ // add src
448
+ $value[ 'sizes' ][ $image_size ] = $src[0];
449
+ $value[ 'sizes' ][ $image_size . '-width' ] = $src[1];
450
+ $value[ 'sizes' ][ $image_size . '-height' ] = $src[2];
451
+ }
452
+ // foreach( $image_sizes as $image_size )
453
  }
 
454
  }
455
+ elseif(is_array( $data->cropped_image)){
456
+ $value = array(
457
+ 'url' => $this->getAbsoluteImageUrl($data->cropped_image['image']),
458
+ );
459
+ }
460
+ else{
461
+
462
+ //echo 'ELSE';
463
+ }
464
+
465
+ }
466
  return $value;
467
+
468
  }
469
 
470
  /*
484
  // Note: This function can be removed if not used
485
 
486
 
487
+ // register acf scripts
488
  wp_register_script('acf-input-image_crop', $this->settings['dir'] . 'js/input.js', array('acf-input', 'imgareaselect'), $this->settings['version']);
489
 
490
+ wp_register_style('acf-input-image_crop', $this->settings['dir'] . 'css/input.css', array('acf-input'), $this->settings['version']);
491
  wp_register_script( 'jcrop', includes_url( 'js/jcrop/jquery.Jcrop.min.css' ));
492
 
493
 
494
+ // scripts
495
+ wp_enqueue_script(array(
496
  'acf-input-image_crop'
497
  ));
498
 
529
  wp_enqueue_style( 'acf-input-image-crop-options');
530
  }
531
 
532
+
533
  /*
534
  * update_value()
535
  *
545
  *
546
  * @return $value - the modified value
547
  */
548
+
549
  function update_value( $value, $post_id, $field )
550
  {
551
  // array?
552
  if( is_array($value) && isset($value['id']) )
553
  {
554
+ $value = $value['id'];
555
  }
556
+
557
  // object?
558
  if( is_object($value) && isset($value->ID) )
559
  {
560
  $value = $value->ID;
561
  }
562
+
563
  return $value;
564
  }
565
 
566
+ function get_image_data($field){
567
+ $imageData = new stdClass();
568
+ $imageData->original_image = '';
569
+ $imageData->original_image_width = '';
570
+ $imageData->original_image_height = '';
571
+ $imageData->cropped_image = '';
572
+ $imageData->original_image_url = '';
573
+ $imageData->preview_image_url = '';
574
+ $imageData->image_url = '';
575
+
576
+ if($field['value'] == ''){
577
+ // Field has not yet been saved or is an empty image field
578
+ return $imageData;
579
+ }
580
+
581
+ $data = json_decode($field['value']);
582
+
583
+ if(! is_object($data)){
584
+ // Field was saved as a regular image field
585
+ $imageAtts = wp_get_attachment_image_src($field['value'], 'full');
586
+ $imageData->original_image = $field['value'];
587
+ $imageData->original_image_width = $imageAtts[1];
588
+ $imageData->original_image_height = $imageAtts[2];
589
+ $imageData->preview_image_url = $this->get_image_src($field['value'], $field['preview_size']);
590
+ $imageData->image_url = $this->get_image_src($field['value'], 'full');
591
+ return $imageData;
592
+ }
593
+
594
+ if( !is_numeric($data->original_image) )
595
+ {
596
+ // The field has been saved, but has no image
597
+ return $imageData;
598
+ }
599
+
600
+ // By now, we have at least a saved original image
601
+ $imageAtts = wp_get_attachment_image_src($data->original_image, 'full');
602
+ $imageData->original_image = $data->original_image;
603
+ $imageData->original_image_width = $imageAtts[1];
604
+ $imageData->original_image_height = $imageAtts[2];
605
+ $imageData->original_image_url = $this->get_image_src($data->original_image, 'full');
606
+
607
+ // Set defaults to original image
608
+ $imageData->image_url = $this->get_image_src($data->original_image, 'full');
609
+ $imageData->preview_image_url = $this->get_image_src($data->original_image, $field['preview_size']);
610
+
611
+ // Check if there is a cropped version and set appropriate attributes
612
+ if(is_numeric($data->cropped_image)){
613
+ // Cropped image was saved to media library ans has an ID
614
+ $imageData->cropped_image = $data->cropped_image;
615
+ $imageData->image_url = $this->get_image_src($data->cropped_image, 'full');
616
+ $imageData->preview_image_url = $this->get_image_src($data->cropped_image, $field['preview_size']);
617
+ }
618
+ elseif(is_object($data->cropped_image)){
619
+ // Cropped image was not saved to media library and is only stored by its URL
620
+ $imageData->cropped_image = $data->cropped_image;
621
+
622
+ // Generate appropriate URLs
623
+ $mediaDir = wp_upload_dir();
624
+ $imageData->image_url = $mediaDir['baseurl'] . '/' . $data->cropped_image->image;
625
+ $imageData->preview_image_url = $mediaDir['baseurl'] . '/' . $data->cropped_image->preview;
626
+ }
627
+ return $imageData;
628
+ }
629
+
630
  function perform_crop(){
631
  $targetWidth = $_POST['target_width'];
632
+ $targetHeight = $_POST['target_height'];
633
+ $saveToMediaLibrary = $_POST['save_to_media_library'] == 'yes';
634
+ $imageData = $this->generate_cropped_image($_POST['id'], $_POST['x1'], $_POST['x2'], $_POST['y1'], $_POST['y2'], $targetWidth, $targetHeight, $saveToMediaLibrary, $_POST['preview_size']);
635
+ // $previewUrl = wp_get_attachment_image_src( $id, $_POST['preview_size']);
636
+ // $fullUrl = wp_get_attachment_image_src( $id, 'full');
637
+ echo json_encode($imageData);
 
 
 
 
638
  die();
639
  }
640
 
641
+ function generate_cropped_image($id, $x1, $x2, $y1, $y2, $targetW, $targetH, $saveToMediaLibrary, $previewSize){//$id, $x1, $x2, $y$, $y2, $targetW, $targetH){
642
  require_once ABSPATH . "/wp-admin/includes/file.php";
643
+ require_once ABSPATH . "/wp-admin/includes/image.php";
644
+
645
+ // Create the variable that will hold the new image data
646
+ $imageData = array();
647
+
648
+ // Fetch media library info
649
+ $mediaDir = wp_upload_dir();
650
+
651
+ // Get original image info
652
+ $originalImageData = wp_get_attachment_metadata($id);
653
+
654
+ // Get image editor from original image path to crop the image
655
+ $image = wp_get_image_editor( $mediaDir['basedir'] . '/' . $originalImageData['file'] );
656
+
657
+ // Crop the image using the provided measurements
658
+ $image->crop($x1, $y1, $x2 - $x1, $y2 - $y1, $targetW, $targetH);
659
+
660
+ // Retrieve original filename and seperate it from its file extension
661
+ $originalFileName = explode('.', basename($originalImageData['file']));
662
+
663
+ // Generate new base filename
664
+ $targetFileName = $originalFileName[0] . '_' . $targetW . 'x' . $targetH . '_acf_cropped' . '.' . $originalFileName[1];
665
+
666
+ // Generate target path new file using existing media library
667
+ $targetFilePath = $mediaDir['path'] . '/' . wp_unique_filename( $mediaDir['path'], $targetFileName);
668
+
669
+ // Get the relative path to save as the actual image url
670
+ $targetRelativePath = str_replace($mediaDir['basedir'] . '/', '', $targetFilePath);
671
+
672
+ // Save the image to the target path
673
+ if(is_wp_error($image->save($targetFilePath))){
674
+ // There was an error saving the image
675
+ //TODO handle it
676
+ }
677
+
678
+ // If file should be saved to media library, create an attachment for it at get the new attachment ID
679
+ if($saveToMediaLibrary){
680
+ // Generate attachment from created file
681
+
682
+ // Get the filetype
683
+ $wp_filetype = wp_check_filetype(basename($targetFilePath), null );
684
+ $attachment = array(
685
+ 'guid' => $targetFilePath,
686
+ 'post_mime_type' => $wp_filetype['type'],
687
+ 'post_title' => preg_replace('/\.[^.]+$/', '', basename($targetFilePath)),
688
+ 'post_content' => '',
689
+ 'post_status' => 'inherit'
690
+ );
691
+ $attachmentId = wp_insert_attachment( $attachment, $targetFilePath);
692
+ $attachmentData = wp_generate_attachment_metadata( $attachmentId, $targetFilePath );
693
+ wp_update_attachment_metadata( $attachmentId, $attachmentData );
694
+
695
+ // Add the id to the imageData-array
696
+ $imageData['value'] = $attachmentId;
697
+
698
+ // Add the image url
699
+ $imageUrlObject = wp_get_attachment_image_src( $attachmentId, 'full');
700
+ $imageData['url'] = $imageUrlObject[0];
701
+
702
+ // Add the preview url as well
703
+ $previewUrlObject = wp_get_attachment_image_src( $attachmentId, $previewSize);
704
+ $imageData['preview_url'] = $previewUrlObject[0];
705
+ }
706
+ // Else we need to return the actual path of the cropped image
707
+ else{
708
+ // Add the image url to the imageData-array
709
+ $imageData['value'] = array('image' => $targetRelativePath);
710
+ $imageData['url'] = $mediaDir['baseurl'] . '/' . $targetRelativePath;
711
+
712
+ // Get preview size dimensions
713
+ global $_wp_additional_image_sizes;
714
+ $previewWidth = 0;
715
+ $previewHeight = 0;
716
+ $crop = 0;
717
+ if (isset($_wp_additional_image_sizes[$previewSize])) {
718
+ $previewWidth = intval($_wp_additional_image_sizes[$previewSize]['width']);
719
+ $previewHeight = intval($_wp_additional_image_sizes[$previewSize]['height']);
720
+ $crop = $_wp_additional_image_sizes[$previewSize]['crop'];
721
+ } else {
722
+ $previewWidth = get_option($previewSize.'_size_w');
723
+ $previewHeight = get_option($previewSize.'_size_h');
724
+ $crop = get_option($previewSize.'_crop');
725
+ }
726
+
727
+ // Generate preview file path
728
+ $previewFilePath = $mediaDir['path'] . '/' . wp_unique_filename( $mediaDir['path'], 'preview_' . $targetFileName);
729
+ $previewRelativePath = str_replace($mediaDir['basedir'] . '/', '', $previewFilePath);
730
+
731
+ // Get image editor from cropped image
732
+ $croppedImage = wp_get_image_editor( $targetFilePath );
733
+ $croppedImage->resize($previewWidth, $previewHeight, $crop);
734
+
735
+ // Save the preview
736
+ $croppedImage->save($previewFilePath);
737
+
738
+ // Add the preview url
739
+ $imageData['preview_url'] = $mediaDir['baseurl'] . '/' . $previewRelativePath;
740
+ $imageData['value']['preview'] = $previewRelativePath;
741
+ }
742
+ return $imageData;
743
+ }
744
+
745
+ function get_image_src($id, $size = 'thumbnail'){
746
+ $atts = wp_get_attachment_image_src( $id, $size);
747
+ return $atts[0];
748
+ }
749
+
750
+ function getAbsoluteImageUrl($relativeUrl){
751
+ $mediaDir = wp_upload_dir();
752
+ return $mediaDir['baseurl'] . '/' . $relativeUrl;
753
  }
754
 
755
  }
js/input.js CHANGED
@@ -1,10 +1,10 @@
1
  (function($){
2
-
3
-
4
  /*
5
  * acf/setup_fields
6
  *
7
- * This event is triggered when ACF adds any new elements to the DOM.
8
  *
9
  * @type function
10
  * @since 1.0.0
@@ -15,10 +15,10 @@
15
  *
16
  * @return N/A
17
  */
18
-
19
- $(document).on('acf/setup_fields', function(e, postbox){
20
  $(postbox).find('.field_type-image_crop').each(function(){
21
- var $field = $(this), $options = $field.find('.acf-image-uploader');
22
  $field.find('.acf-image-value').on('change', function(){
23
  var originalImage = $(this).val();
24
  if($(this).val()){
@@ -26,14 +26,14 @@
26
  $field.find('.init-crop-button').removeAttr('disabled');
27
  $field.find('.acf-image-value').data('original-image', originalImage);
28
  $field.find('.acf-image-value').data('cropped-image', originalImage);
29
- $field.find('.acf-image-value').data('cropped', false);
30
  $.post(ajaxurl, {action: 'acf_image_crop_get_image_size', image_id: originalImage}, function(data, textStatus, xhr) {
31
  if($field.find('img.crop-image').length == 0){
32
  $field.find('.crop-action').append($('<img class="crop-image" src="#"/>'));
33
  }
34
- $field.find('img.crop-image').attr('src', data['url']);
35
- $field.find('img.crop-image').data('width', data['width']);
36
- $field.find('img.crop-image').data('height', data['height']);
37
  var warnings = [];
38
  var valid = true;
39
  if($options.data('width') && data['width'] < $options.data('width')){
@@ -52,18 +52,18 @@
52
  else{
53
  if($options.data('force-crop')){
54
  initCrop($field);
55
- }
56
  }
57
-
58
  }, 'json');
59
  updateFieldValue($field);
60
  }
61
  else{
62
  //Do nothing
63
  }
64
-
65
- })
66
- $field.find('.init-crop-button').click(function(e){
67
  e.preventDefault();
68
  initCrop($field);
69
  });
@@ -75,17 +75,17 @@
75
  e.preventDefault();
76
  cancelCrop($field);
77
  });
78
- });
79
-
80
  });
81
 
82
  function initCrop($field){
83
  var $options = $field.find('.acf-image-uploader');
84
  var options = {
85
  handles: true,
86
- onSelectEnd: function (img, selection) {
87
- updateThumbnail($field, img, selection);
88
- updateCropData($field, img, selection);
89
  },
90
  imageWidth:$options.find('.crop-stage img.crop-image').data('width'),
91
  imageHeight:$options.find('.crop-stage img.crop-image').data('height'),
@@ -97,26 +97,32 @@
97
  options.minWidth = $options.data('width');
98
  options.minHeight = $options.data('height');
99
  options.x2 = $options.data('width');
100
- options.y2 = $options.data('height');
101
  }
102
  else if($options.data('crop-type') == 'min'){
103
  if($options.data('width')){
104
- options.minWidth = $options.data('width');
105
  options.x2 = $options.data('width');
106
  }
107
  else{
108
  options.x2 = options.imageWidth;
109
  }
110
  if($options.data('height')){
111
- options.minHeight = $options.data('height');
112
  options.y2 = $options.data('height');
113
  }
114
  else{
115
  options.y2 = options.imageHeight;
116
- }
117
  }
 
 
 
 
 
 
118
  if(!$field.hasClass('invalid')){
119
- toggleCropView($field);
120
  $field.find('.crop-stage img.crop-image').imgAreaSelect(options);
121
  updateCropData($field, $field.find('.crop-stage img.crop-image').get(0), {y1: options.y1, y2: options.y2, x1: options.x1, x2: options.x2});
122
  updateThumbnail($field, $field.find('.crop-stage img.crop-image').get(0), {y1: options.y1, y2: options.y2, x1: options.x1, x2: options.x2});
@@ -139,8 +145,8 @@
139
  //image
140
  div.css('background-image', 'url(' + img.src + ')');
141
  //width
142
- div.css('width', (selection.x2 - selection.x1) * factor);
143
- //height
144
  div.css('height', (selection.y2 - selection.y1) * factor);
145
  //x offset
146
  div.css('background-position-x', 0-(selection.x1 * factor));
@@ -157,12 +163,13 @@
157
  return JSON.stringify(obj);
158
  }
159
 
160
- function performCrop($field){
161
  if(!$field.find('.crop-stage').hasClass('loading')){
162
  $field.find('.crop-stage').addClass('loading');
163
  var $options = $field.find('.acf-image-uploader');
164
  var targetWidth = $options.data('width');
165
  var targetHeight = $options.data('height');
 
166
  if($options.data('crop-type') == 'min'){
167
  targetWidth = $options.data('x2') - $options.data('x1');
168
  targetHeight = $options.data('y2') - $options.data('y1');
@@ -176,21 +183,22 @@
176
  y2: $options.data('y2'),
177
  target_width: targetWidth,
178
  target_height: targetHeight,
179
- preview_size: $options.data('preview_size')
180
- }
 
181
  $.post(ajaxurl, data, function(data, textStatus, xhr) {
182
- $field.find('.acf-image-image').attr('src', data.url_preview);
183
- $field.find('.acf-image-value').data('cropped-image', data.id);
184
- $field.find('.acf-image-value').data('cropped', true);
185
- updateFieldValue($field);
186
- $field.find('.crop-stage').removeClass('loading');
187
  cancelCrop($field);
188
  }, 'json');
189
  }
190
  }
191
 
192
  function cancelCrop($field){
193
- toggleCropView($field);
194
  $field.find('.crop-stage img.crop-image').imgAreaSelect({remove:true});
195
  }
196
 
@@ -199,14 +207,14 @@
199
  $('#acf-image-crop-overlay').remove();
200
  }
201
  else{
202
- $('body').append($('<div id="acf-image-crop-overlay"></div>'));
203
  }
204
- $field.toggleClass('cropping');
205
 
206
  }
207
 
208
  function updateFieldValue($field){
209
- var $input = $field.find('.acf-image-value');
210
  $input.val(generateCropJSON($input.data('original-image'), $input.data('cropped-image')));
211
  }
212
 
1
  (function($){
2
+
3
+
4
  /*
5
  * acf/setup_fields
6
  *
7
+ * This event is triggered when ACF adds any new elements to the DOM.
8
  *
9
  * @type function
10
  * @since 1.0.0
15
  *
16
  * @return N/A
17
  */
18
+
19
+ $(document).on('acf/setup_fields', function(e, postbox){
20
  $(postbox).find('.field_type-image_crop').each(function(){
21
+ var $field = $(this), $options = $field.find('.acf-image-uploader');
22
  $field.find('.acf-image-value').on('change', function(){
23
  var originalImage = $(this).val();
24
  if($(this).val()){
26
  $field.find('.init-crop-button').removeAttr('disabled');
27
  $field.find('.acf-image-value').data('original-image', originalImage);
28
  $field.find('.acf-image-value').data('cropped-image', originalImage);
29
+ $field.find('.acf-image-value').data('cropped', false);
30
  $.post(ajaxurl, {action: 'acf_image_crop_get_image_size', image_id: originalImage}, function(data, textStatus, xhr) {
31
  if($field.find('img.crop-image').length == 0){
32
  $field.find('.crop-action').append($('<img class="crop-image" src="#"/>'));
33
  }
34
+ $field.find('img.crop-image').attr('src', data['url']);
35
+ $field.find('img.crop-image').data('width', data['width']);
36
+ $field.find('img.crop-image').data('height', data['height']);
37
  var warnings = [];
38
  var valid = true;
39
  if($options.data('width') && data['width'] < $options.data('width')){
52
  else{
53
  if($options.data('force-crop')){
54
  initCrop($field);
55
+ }
56
  }
57
+
58
  }, 'json');
59
  updateFieldValue($field);
60
  }
61
  else{
62
  //Do nothing
63
  }
64
+
65
+ })
66
+ $field.find('.init-crop-button').click(function(e){
67
  e.preventDefault();
68
  initCrop($field);
69
  });
75
  e.preventDefault();
76
  cancelCrop($field);
77
  });
78
+ });
79
+
80
  });
81
 
82
  function initCrop($field){
83
  var $options = $field.find('.acf-image-uploader');
84
  var options = {
85
  handles: true,
86
+ onSelectEnd: function (img, selection) {
87
+ updateThumbnail($field, img, selection);
88
+ updateCropData($field, img, selection);
89
  },
90
  imageWidth:$options.find('.crop-stage img.crop-image').data('width'),
91
  imageHeight:$options.find('.crop-stage img.crop-image').data('height'),
97
  options.minWidth = $options.data('width');
98
  options.minHeight = $options.data('height');
99
  options.x2 = $options.data('width');
100
+ options.y2 = $options.data('height');
101
  }
102
  else if($options.data('crop-type') == 'min'){
103
  if($options.data('width')){
104
+ options.minWidth = $options.data('width');
105
  options.x2 = $options.data('width');
106
  }
107
  else{
108
  options.x2 = options.imageWidth;
109
  }
110
  if($options.data('height')){
111
+ options.minHeight = $options.data('height');
112
  options.y2 = $options.data('height');
113
  }
114
  else{
115
  options.y2 = options.imageHeight;
116
+ }
117
  }
118
+ // Center crop - disabled needs more testing
119
+ // options.x1 = options.imageWidth/2 - (options.minWidth/2);
120
+ // options.y1 = options.imageHeight/2 - (options.minHeight/2)
121
+ // options.x2 = options.minWidth + options.x1;
122
+ // options.y2 = options.minHeight + options.y1;
123
+ //options.y1 = (options.imageHeight - options.minHeight) / 2;
124
  if(!$field.hasClass('invalid')){
125
+ toggleCropView($field);
126
  $field.find('.crop-stage img.crop-image').imgAreaSelect(options);
127
  updateCropData($field, $field.find('.crop-stage img.crop-image').get(0), {y1: options.y1, y2: options.y2, x1: options.x1, x2: options.x2});
128
  updateThumbnail($field, $field.find('.crop-stage img.crop-image').get(0), {y1: options.y1, y2: options.y2, x1: options.x1, x2: options.x2});
145
  //image
146
  div.css('background-image', 'url(' + img.src + ')');
147
  //width
148
+ div.css('width', (selection.x2 - selection.x1) * factor);
149
+ //height
150
  div.css('height', (selection.y2 - selection.y1) * factor);
151
  //x offset
152
  div.css('background-position-x', 0-(selection.x1 * factor));
163
  return JSON.stringify(obj);
164
  }
165
 
166
+ function performCrop($field){
167
  if(!$field.find('.crop-stage').hasClass('loading')){
168
  $field.find('.crop-stage').addClass('loading');
169
  var $options = $field.find('.acf-image-uploader');
170
  var targetWidth = $options.data('width');
171
  var targetHeight = $options.data('height');
172
+ var saveToMediaLibrary = $options.data('save-to-media-library');
173
  if($options.data('crop-type') == 'min'){
174
  targetWidth = $options.data('x2') - $options.data('x1');
175
  targetHeight = $options.data('y2') - $options.data('y1');
183
  y2: $options.data('y2'),
184
  target_width: targetWidth,
185
  target_height: targetHeight,
186
+ preview_size: $options.data('preview_size'),
187
+ save_to_media_library: saveToMediaLibrary
188
+ }
189
  $.post(ajaxurl, data, function(data, textStatus, xhr) {
190
+ $field.find('.acf-image-image').attr('src', data.preview_url);
191
+ $field.find('.acf-image-value').data('cropped-image', data.value);
192
+ $field.find('.acf-image-value').data('cropped', true);
193
+ updateFieldValue($field);
194
+ $field.find('.crop-stage').removeClass('loading');
195
  cancelCrop($field);
196
  }, 'json');
197
  }
198
  }
199
 
200
  function cancelCrop($field){
201
+ toggleCropView($field);
202
  $field.find('.crop-stage img.crop-image').imgAreaSelect({remove:true});
203
  }
204
 
207
  $('#acf-image-crop-overlay').remove();
208
  }
209
  else{
210
+ $('body').append($('<div id="acf-image-crop-overlay"></div>'));
211
  }
212
+ $field.toggleClass('cropping');
213
 
214
  }
215
 
216
  function updateFieldValue($field){
217
+ var $input = $field.find('.acf-image-value');
218
  $input.val(generateCropJSON($input.data('original-image'), $input.data('cropped-image')));
219
  }
220
 
js/options.js CHANGED
@@ -8,10 +8,24 @@ jQuery(function($){
8
  }
9
 
10
  });
11
- $(document).on('change', '.field_type-image_crop .crop-type-select', function(e) {
12
-
13
  $(this).parents('.field_type-image_crop').find('.dimensions-wrap .dimensions-description').addClass('hidden');
14
  $(this).parents('.field_type-image_crop').find('.dimensions-wrap .dimensions-description[data-type=' + $(this).val() + ']').removeClass('hidden');
15
 
16
  });
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
17
  });
8
  }
9
 
10
  });
11
+ $(document).on('change', '.field_type-image_crop .crop-type-select', function(e) {
 
12
  $(this).parents('.field_type-image_crop').find('.dimensions-wrap .dimensions-description').addClass('hidden');
13
  $(this).parents('.field_type-image_crop').find('.dimensions-wrap .dimensions-description[data-type=' + $(this).val() + ']').removeClass('hidden');
14
 
15
  });
16
+ $(document).on('click', '.field_type-image_crop .save-in-media-library-select input', function(e) {
17
+ var saveToMedia = $(this).val() == 'yes';
18
+ var $returnValueField = $(this).parents('.field_type-image_crop').find('.return-value-select');
19
+ if(! saveToMedia){
20
+ $returnValueField.find('input[value=id], input[value=object]').attr('disabled', true).parents('label').addClass('disabled');
21
+ $returnValueField.find('input[value=url]').attr('checked', true);
22
+ }
23
+ else{
24
+ $returnValueField.find('input').removeAttr('disabled').parents('label').removeClass('disabled');
25
+ }
26
+
27
+ // $(this).parents('.field_type-image_crop').find('.dimensions-wrap .dimensions-description').addClass('hidden');
28
+ // $(this).parents('.field_type-image_crop').find('.dimensions-wrap .dimensions-description[data-type=' + $(this).val() + ']').removeClass('hidden');
29
+
30
+ });
31
  });
readme.txt CHANGED
@@ -2,8 +2,8 @@
2
  Contributors: andersthorborg
3
  Tags: afc, advanced custom fields, image crop, image, crop
4
  Requires at least: 3.5
5
- Tested up to: 3.8.1
6
- Stable tag: 0.8
7
  License: GPLv2 or later
8
  License URI: http://www.gnu.org/licenses/gpl-2.0.html
9
 
@@ -16,9 +16,11 @@ The field gives the developer/administrator the option to predefine a size for t
16
 
17
  The plugin supports the defined image sizes as well as a custom option, enabling the developer to specify the dimensions from within the field edit screen.
18
 
19
- The field can be configured to enforce a hard crop or a minimal-dimension-based crop. The hard crop will lock the aspect ratio of the crop where as the minimal-dimension-based crop will not allow the user to crop the image below the specified dimensions.
20
 
21
- This plugin diverts from plugins like [Manual Image Crop](http://wordpress.org/plugins/manual-image-crop/) in that when the user crops an image, a new attachment is generated, so that the relevant crop only applies in the context it is edited. It also spares the user from dealing with the concept of various image sizes. (This does however have the potential of over-crowding the media-directory with differently cropped versions of images).
 
 
22
 
23
  = Compatibility =
24
 
@@ -59,6 +61,10 @@ function my_register_fields()
59
 
60
  == Changelog ==
61
 
 
 
 
 
62
  = 0.8 =
63
  * Fixed an issue resulting in a black image, when image was cropped without moving the crop handles
64
 
2
  Contributors: andersthorborg
3
  Tags: afc, advanced custom fields, image crop, image, crop
4
  Requires at least: 3.5
5
+ Tested up to: 3.9.1
6
+ Stable tag: 1.0
7
  License: GPLv2 or later
8
  License URI: http://www.gnu.org/licenses/gpl-2.0.html
9
 
16
 
17
  The plugin supports the defined image sizes as well as a custom option, enabling the developer to specify the dimensions from within the field edit screen.
18
 
19
+ The field can be configured to enforce a hard crop or a minimal-dimension-based crop. The hard crop will lock the aspect ratio of the crop where as the minimal-dimension-based crop will not allow the user to crop the image below the specified dimensions.
20
 
21
+ This plugin diverts from plugins like [Manual Image Crop](http://wordpress.org/plugins/manual-image-crop/) in that when the user crops an image, a new attachment is generated, so that the relevant crop only applies in the context it is edited. It also keeps the user from dealing with the concept of various image sizes.
22
+
23
+ As of version 1.0 the field can be configured to either create the cropped image as a media-item (the default behavior) or simply create it and refer directly to the file without adding it to the media library. This will prevent the media library from being cluttered with several cropped versions of the same image. When this option is selected the only available return type for the field is URL.
24
 
25
  = Compatibility =
26
 
61
 
62
  == Changelog ==
63
 
64
+ = 1.0 =
65
+ * Added option to save the image to media library or refer directly to the created image, not using the media library.
66
+ * Added better compatibility with the native image field making it possible to migrate from the regular image field to the crop-image field without losing the images currently attached. (It doesn't work the other way around)
67
+
68
  = 0.8 =
69
  * Fixed an issue resulting in a black image, when image was cropped without moving the crop handles
70
 
screenshot-1.png ADDED
Binary file
screenshot-2.png ADDED
Binary file
screenshot-3.png ADDED
Binary file
screenshot-4.png ADDED
Binary file
screenshot-5.png ADDED
Binary file
screenshot-6.png ADDED
Binary file