WordPress 3.4
[autoinstalls/wordpress.git] / wp-admin / includes / image-edit.php
1 <?php
2 /**
3  * WordPress Image Editor
4  *
5  * @package WordPress
6  * @subpackage Administration
7  */
8
9 function wp_image_editor($post_id, $msg = false) {
10         $nonce = wp_create_nonce("image_editor-$post_id");
11         $meta = wp_get_attachment_metadata($post_id);
12         $thumb = image_get_intermediate_size($post_id, 'thumbnail');
13         $sub_sizes = isset($meta['sizes']) && is_array($meta['sizes']);
14         $note = '';
15
16         if ( is_array($meta) && isset($meta['width']) )
17                 $big = max( $meta['width'], $meta['height'] );
18         else
19                 die( __('Image data does not exist. Please re-upload the image.') );
20
21         $sizer = $big > 400 ? 400 / $big : 1;
22
23         $backup_sizes = get_post_meta( $post_id, '_wp_attachment_backup_sizes', true );
24         $can_restore = !empty($backup_sizes) && isset($backup_sizes['full-orig'])
25                 && $backup_sizes['full-orig']['file'] != basename($meta['file']);
26
27         if ( $msg ) {
28                 if ( isset($msg->error) )
29                         $note = "<div class='error'><p>$msg->error</p></div>";
30                 elseif ( isset($msg->msg) )
31                         $note = "<div class='updated'><p>$msg->msg</p></div>";
32         }
33
34         ?>
35         <div class="imgedit-wrap">
36         <?php echo $note; ?>
37         <table id="imgedit-panel-<?php echo $post_id; ?>"><tbody>
38         <tr><td>
39         <div class="imgedit-menu">
40                 <div onclick="imageEdit.crop(<?php echo "$post_id, '$nonce'"; ?>, this)" class="imgedit-crop disabled" title="<?php esc_attr_e( 'Crop' ); ?>"></div><?php
41
42         // On some setups GD library does not provide imagerotate() - Ticket #11536
43         if ( function_exists('imagerotate') ) { ?>
44                 <div class="imgedit-rleft"  onclick="imageEdit.rotate( 90, <?php echo "$post_id, '$nonce'"; ?>, this)" title="<?php esc_attr_e( 'Rotate counter-clockwise' ); ?>"></div>
45                 <div class="imgedit-rright" onclick="imageEdit.rotate(-90, <?php echo "$post_id, '$nonce'"; ?>, this)" title="<?php esc_attr_e( 'Rotate clockwise' ); ?>"></div>
46 <?php } else {
47                 $note_gdlib = esc_attr__('Image rotation is not supported by your web host (function imagerotate() is missing)');
48 ?>
49             <div class="imgedit-rleft disabled"  title="<?php echo $note_gdlib; ?>"></div>
50             <div class="imgedit-rright disabled" title="<?php echo $note_gdlib; ?>"></div>
51 <?php } ?>
52
53                 <div onclick="imageEdit.flip(1, <?php echo "$post_id, '$nonce'"; ?>, this)" class="imgedit-flipv" title="<?php esc_attr_e( 'Flip vertically' ); ?>"></div>
54                 <div onclick="imageEdit.flip(2, <?php echo "$post_id, '$nonce'"; ?>, this)" class="imgedit-fliph" title="<?php esc_attr_e( 'Flip horizontally' ); ?>"></div>
55
56                 <div id="image-undo-<?php echo $post_id; ?>" onclick="imageEdit.undo(<?php echo "$post_id, '$nonce'"; ?>, this)" class="imgedit-undo disabled" title="<?php esc_attr_e( 'Undo' ); ?>"></div>
57                 <div id="image-redo-<?php echo $post_id; ?>" onclick="imageEdit.redo(<?php echo "$post_id, '$nonce'"; ?>, this)" class="imgedit-redo disabled" title="<?php esc_attr_e( 'Redo' ); ?>"></div>
58                 <br class="clear" />
59         </div>
60
61         <input type="hidden" id="imgedit-sizer-<?php echo $post_id; ?>" value="<?php echo $sizer; ?>" />
62         <input type="hidden" id="imgedit-minthumb-<?php echo $post_id; ?>" value="<?php echo ( get_option('thumbnail_size_w') . ':' . get_option('thumbnail_size_h') ); ?>" />
63         <input type="hidden" id="imgedit-history-<?php echo $post_id; ?>" value="" />
64         <input type="hidden" id="imgedit-undone-<?php echo $post_id; ?>" value="0" />
65         <input type="hidden" id="imgedit-selection-<?php echo $post_id; ?>" value="" />
66         <input type="hidden" id="imgedit-x-<?php echo $post_id; ?>" value="<?php echo $meta['width']; ?>" />
67         <input type="hidden" id="imgedit-y-<?php echo $post_id; ?>" value="<?php echo $meta['height']; ?>" />
68
69         <div id="imgedit-crop-<?php echo $post_id; ?>" class="imgedit-crop-wrap">
70         <img id="image-preview-<?php echo $post_id; ?>" onload="imageEdit.imgLoaded('<?php echo $post_id; ?>')" src="<?php echo admin_url( 'admin-ajax.php', 'relative' ); ?>?action=imgedit-preview&amp;_ajax_nonce=<?php echo $nonce; ?>&amp;postid=<?php echo $post_id; ?>&amp;rand=<?php echo rand(1, 99999); ?>" />
71         </div>
72
73         <div class="imgedit-submit">
74                 <input type="button" onclick="imageEdit.close(<?php echo $post_id; ?>, 1)" class="button" value="<?php esc_attr_e( 'Cancel' ); ?>" />
75                 <input type="button" onclick="imageEdit.save(<?php echo "$post_id, '$nonce'"; ?>)" disabled="disabled" class="button-primary imgedit-submit-btn" value="<?php esc_attr_e( 'Save' ); ?>" />
76         </div>
77         </td>
78
79         <td class="imgedit-settings">
80         <div class="imgedit-group">
81         <div class="imgedit-group-top">
82                 <a class="imgedit-help-toggle" onclick="imageEdit.toggleHelp(this);return false;" href="#"><strong><?php _e('Scale Image'); ?></strong></a>
83                 <div class="imgedit-help">
84                 <p><?php _e('You can proportionally scale the original image. For best results the scaling should be done before performing any other operations on it like crop, rotate, etc. Note that if you make the image larger it may become fuzzy.'); ?></p>
85                 <p><?php printf( __('Original dimensions %s'), $meta['width'] . '&times;' . $meta['height'] ); ?></p>
86                 <div class="imgedit-submit">
87                 <span class="nowrap"><input type="text" id="imgedit-scale-width-<?php echo $post_id; ?>" onkeyup="imageEdit.scaleChanged(<?php echo $post_id; ?>, 1)" onblur="imageEdit.scaleChanged(<?php echo $post_id; ?>, 1)" style="width:4em;" value="<?php echo $meta['width']; ?>" />&times;<input type="text" id="imgedit-scale-height-<?php echo $post_id; ?>" onkeyup="imageEdit.scaleChanged(<?php echo $post_id; ?>, 0)" onblur="imageEdit.scaleChanged(<?php echo $post_id; ?>, 0)" style="width:4em;" value="<?php echo $meta['height']; ?>" />
88                 <span class="imgedit-scale-warn" id="imgedit-scale-warn-<?php echo $post_id; ?>">!</span></span>
89                 <input type="button" onclick="imageEdit.action(<?php echo "$post_id, '$nonce'"; ?>, 'scale')" class="button-primary" value="<?php esc_attr_e( 'Scale' ); ?>" />
90                 </div>
91                 </div>
92         </div>
93
94 <?php if ( $can_restore ) { ?>
95
96         <div class="imgedit-group-top">
97                 <a class="imgedit-help-toggle" onclick="imageEdit.toggleHelp(this);return false;" href="#"><strong><?php _e('Restore Original Image'); ?></strong></a>
98                 <div class="imgedit-help">
99                 <p><?php _e('Discard any changes and restore the original image.');
100
101                 if ( !defined('IMAGE_EDIT_OVERWRITE') || !IMAGE_EDIT_OVERWRITE )
102                         echo ' '.__('Previously edited copies of the image will not be deleted.');
103
104                 ?></p>
105                 <div class="imgedit-submit">
106                 <input type="button" onclick="imageEdit.action(<?php echo "$post_id, '$nonce'"; ?>, 'restore')" class="button-primary" value="<?php esc_attr_e( 'Restore image' ); ?>" <?php echo $can_restore; ?> />
107                 </div>
108                 </div>
109         </div>
110
111 <?php } ?>
112
113         </div>
114
115         <div class="imgedit-group">
116         <div class="imgedit-group-top">
117                 <strong><?php _e('Image Crop'); ?></strong>
118                 <a class="imgedit-help-toggle" onclick="imageEdit.toggleHelp(this);return false;" href="#"><?php _e('(help)'); ?></a>
119                 <div class="imgedit-help">
120                 <p><?php _e('The image can be cropped by clicking on it and dragging to select the desired part. While dragging the dimensions of the selection are displayed below.'); ?></p>
121                 <strong><?php _e('Keyboard Shortcuts'); ?></strong>
122                 <ul>
123                 <li><?php _e('Arrow: move by 10px'); ?></li>
124                 <li><?php _e('Shift + arrow: move by 1px'); ?></li>
125                 <li><?php _e('Ctrl + arrow: resize by 10px'); ?></li>
126                 <li><?php _e('Ctrl + Shift + arrow: resize by 1px'); ?></li>
127                 <li><?php _e('Shift + drag: lock aspect ratio'); ?></li>
128                 </ul>
129
130                 <p><strong><?php _e('Crop Aspect Ratio'); ?></strong><br />
131                 <?php _e('You can specify the crop selection aspect ratio then hold down the Shift key while dragging to lock it. The values can be 1:1 (square), 4:3, 16:9, etc. If there is a selection, specifying aspect ratio will set it immediately.'); ?></p>
132
133                 <p><strong><?php _e('Crop Selection'); ?></strong><br />
134                 <?php _e('Once started, the selection can be adjusted by entering new values (in pixels). Note that these values are scaled to approximately match the original image dimensions. The minimum selection size equals the thumbnail size as set in the Media settings.'); ?></p>
135                 </div>
136         </div>
137
138         <p>
139                 <?php _e('Aspect ratio:'); ?>
140                 <span  class="nowrap">
141                 <input type="text" id="imgedit-crop-width-<?php echo $post_id; ?>" onkeyup="imageEdit.setRatioSelection(<?php echo $post_id; ?>, 0, this)" style="width:3em;" />
142                 :
143                 <input type="text" id="imgedit-crop-height-<?php echo $post_id; ?>" onkeyup="imageEdit.setRatioSelection(<?php echo $post_id; ?>, 1, this)" style="width:3em;" />
144                 </span>
145         </p>
146
147         <p id="imgedit-crop-sel-<?php echo $post_id; ?>">
148                 <?php _e('Selection:'); ?>
149                 <span  class="nowrap">
150                 <input type="text" id="imgedit-sel-width-<?php echo $post_id; ?>" onkeyup="imageEdit.setNumSelection(<?php echo $post_id; ?>)" style="width:4em;" />
151                 :
152                 <input type="text" id="imgedit-sel-height-<?php echo $post_id; ?>" onkeyup="imageEdit.setNumSelection(<?php echo $post_id; ?>)" style="width:4em;" />
153                 </span>
154         </p>
155         </div>
156
157         <?php if ( $thumb && $sub_sizes ) {
158                 $thumb_img = wp_constrain_dimensions( $thumb['width'], $thumb['height'], 160, 120 );
159         ?>
160
161         <div class="imgedit-group imgedit-applyto">
162         <div class="imgedit-group-top">
163                 <strong><?php _e('Thumbnail Settings'); ?></strong>
164                 <a class="imgedit-help-toggle" onclick="imageEdit.toggleHelp(this);return false;" href="#"><?php _e('(help)'); ?></a>
165                 <p class="imgedit-help"><?php _e('The thumbnail image can be cropped differently. For example it can be square or contain only a portion of the original image to showcase it better. Here you can select whether to apply changes to all image sizes or make the thumbnail different.'); ?></p>
166         </div>
167
168         <p>
169                 <img src="<?php echo $thumb['url']; ?>" width="<?php echo $thumb_img[0]; ?>" height="<?php echo $thumb_img[1]; ?>" class="imgedit-size-preview" alt="" /><br /><?php _e('Current thumbnail'); ?>
170         </p>
171
172         <p id="imgedit-save-target-<?php echo $post_id; ?>">
173                 <strong><?php _e('Apply changes to:'); ?></strong><br />
174
175                 <label class="imgedit-label">
176                 <input type="radio" name="imgedit-target-<?php echo $post_id; ?>" value="all" checked="checked" />
177                 <?php _e('All image sizes'); ?></label>
178
179                 <label class="imgedit-label">
180                 <input type="radio" name="imgedit-target-<?php echo $post_id; ?>" value="thumbnail" />
181                 <?php _e('Thumbnail'); ?></label>
182
183                 <label class="imgedit-label">
184                 <input type="radio" name="imgedit-target-<?php echo $post_id; ?>" value="nothumb" />
185                 <?php _e('All sizes except thumbnail'); ?></label>
186         </p>
187         </div>
188
189         <?php } ?>
190
191         </td></tr>
192         </tbody></table>
193         <div class="imgedit-wait" id="imgedit-wait-<?php echo $post_id; ?>"></div>
194         <script type="text/javascript">imageEdit.init(<?php echo $post_id; ?>);</script>
195         <div class="hidden" id="imgedit-leaving-<?php echo $post_id; ?>"><?php _e("There are unsaved changes that will be lost. 'OK' to continue, 'Cancel' to return to the Image Editor."); ?></div>
196         </div>
197 <?php
198 }
199
200 function wp_stream_image($image, $mime_type, $post_id) {
201         $image = apply_filters('image_save_pre', $image, $post_id);
202
203         switch ( $mime_type ) {
204                 case 'image/jpeg':
205                         header('Content-Type: image/jpeg');
206                         return imagejpeg($image, null, 90);
207                 case 'image/png':
208                         header('Content-Type: image/png');
209                         return imagepng($image);
210                 case 'image/gif':
211                         header('Content-Type: image/gif');
212                         return imagegif($image);
213                 default:
214                         return false;
215         }
216 }
217
218 function wp_save_image_file($filename, $image, $mime_type, $post_id) {
219         $image = apply_filters('image_save_pre', $image, $post_id);
220         $saved = apply_filters('wp_save_image_file', null, $filename, $image, $mime_type, $post_id);
221         if ( null !== $saved )
222                 return $saved;
223
224         switch ( $mime_type ) {
225                 case 'image/jpeg':
226                         return imagejpeg( $image, $filename, apply_filters( 'jpeg_quality', 90, 'edit_image' ) );
227                 case 'image/png':
228                         return imagepng($image, $filename);
229                 case 'image/gif':
230                         return imagegif($image, $filename);
231                 default:
232                         return false;
233         }
234 }
235
236 function _image_get_preview_ratio($w, $h) {
237         $max = max($w, $h);
238         return $max > 400 ? (400 / $max) : 1;
239 }
240
241 function _rotate_image_resource($img, $angle) {
242         if ( function_exists('imagerotate') ) {
243                 $rotated = imagerotate($img, $angle, 0);
244                 if ( is_resource($rotated) ) {
245                         imagedestroy($img);
246                         $img = $rotated;
247                 }
248         }
249         return $img;
250 }
251
252 function _flip_image_resource($img, $horz, $vert) {
253         $w = imagesx($img);
254         $h = imagesy($img);
255         $dst = wp_imagecreatetruecolor($w, $h);
256         if ( is_resource($dst) ) {
257                 $sx = $vert ? ($w - 1) : 0;
258                 $sy = $horz ? ($h - 1) : 0;
259                 $sw = $vert ? -$w : $w;
260                 $sh = $horz ? -$h : $h;
261
262                 if ( imagecopyresampled($dst, $img, 0, 0, $sx, $sy, $w, $h, $sw, $sh) ) {
263                         imagedestroy($img);
264                         $img = $dst;
265                 }
266         }
267         return $img;
268 }
269
270 function _crop_image_resource($img, $x, $y, $w, $h) {
271         $dst = wp_imagecreatetruecolor($w, $h);
272         if ( is_resource($dst) ) {
273                 if ( imagecopy($dst, $img, 0, 0, $x, $y, $w, $h) ) {
274                         imagedestroy($img);
275                         $img = $dst;
276                 }
277         }
278         return $img;
279 }
280
281 function image_edit_apply_changes($img, $changes) {
282
283         if ( !is_array($changes) )
284                 return $img;
285
286         // expand change operations
287         foreach ( $changes as $key => $obj ) {
288                 if ( isset($obj->r) ) {
289                         $obj->type = 'rotate';
290                         $obj->angle = $obj->r;
291                         unset($obj->r);
292                 } elseif ( isset($obj->f) ) {
293                         $obj->type = 'flip';
294                         $obj->axis = $obj->f;
295                         unset($obj->f);
296                 } elseif ( isset($obj->c) ) {
297                         $obj->type = 'crop';
298                         $obj->sel = $obj->c;
299                         unset($obj->c);
300                 }
301                 $changes[$key] = $obj;
302         }
303
304         // combine operations
305         if ( count($changes) > 1 ) {
306                 $filtered = array($changes[0]);
307                 for ( $i = 0, $j = 1; $j < count($changes); $j++ ) {
308                         $combined = false;
309                         if ( $filtered[$i]->type == $changes[$j]->type ) {
310                                 switch ( $filtered[$i]->type ) {
311                                         case 'rotate':
312                                                 $filtered[$i]->angle += $changes[$j]->angle;
313                                                 $combined = true;
314                                                 break;
315                                         case 'flip':
316                                                 $filtered[$i]->axis ^= $changes[$j]->axis;
317                                                 $combined = true;
318                                                 break;
319                                 }
320                         }
321                         if ( !$combined )
322                                 $filtered[++$i] = $changes[$j];
323                 }
324                 $changes = $filtered;
325                 unset($filtered);
326         }
327
328         // image resource before applying the changes
329         $img = apply_filters('image_edit_before_change', $img, $changes);
330
331         foreach ( $changes as $operation ) {
332                 switch ( $operation->type ) {
333                         case 'rotate':
334                                 if ( $operation->angle != 0 )
335                                         $img = _rotate_image_resource($img, $operation->angle);
336                                 break;
337                         case 'flip':
338                                 if ( $operation->axis != 0 )
339                                         $img = _flip_image_resource($img, ($operation->axis & 1) != 0, ($operation->axis & 2) != 0);
340                                 break;
341                         case 'crop':
342                                 $sel = $operation->sel;
343                                 $scale = 1 / _image_get_preview_ratio( imagesx($img), imagesy($img) ); // discard preview scaling
344                                 $img = _crop_image_resource($img, $sel->x * $scale, $sel->y * $scale, $sel->w * $scale, $sel->h * $scale);
345                                 break;
346                 }
347         }
348
349         return $img;
350 }
351
352 function stream_preview_image($post_id) {
353         $post = get_post($post_id);
354         @ini_set( 'memory_limit', apply_filters( 'admin_memory_limit', WP_MAX_MEMORY_LIMIT ) );
355         $img = load_image_to_edit( $post_id, $post->post_mime_type, array(400, 400) );
356
357         if ( !is_resource($img) )
358                 return false;
359
360         $changes = !empty($_REQUEST['history']) ? json_decode( stripslashes($_REQUEST['history']) ) : null;
361         if ( $changes )
362                 $img = image_edit_apply_changes($img, $changes);
363
364         // scale the image
365         $w = imagesx($img);
366         $h = imagesy($img);
367         $ratio = _image_get_preview_ratio($w, $h);
368         $w2 = $w * $ratio;
369         $h2 = $h * $ratio;
370
371         $preview = wp_imagecreatetruecolor($w2, $h2);
372         imagecopyresampled( $preview, $img, 0, 0, 0, 0, $w2, $h2, $w, $h );
373         wp_stream_image($preview, $post->post_mime_type, $post_id);
374
375         imagedestroy($preview);
376         imagedestroy($img);
377         return true;
378 }
379
380 function wp_restore_image($post_id) {
381         $meta = wp_get_attachment_metadata($post_id);
382         $file = get_attached_file($post_id);
383         $backup_sizes = get_post_meta( $post_id, '_wp_attachment_backup_sizes', true );
384         $restored = false;
385         $msg = new stdClass;
386
387         if ( !is_array($backup_sizes) ) {
388                 $msg->error = __('Cannot load image metadata.');
389                 return $msg;
390         }
391
392         $parts = pathinfo($file);
393         $suffix = time() . rand(100, 999);
394         $default_sizes = get_intermediate_image_sizes();
395
396         if ( isset($backup_sizes['full-orig']) && is_array($backup_sizes['full-orig']) ) {
397                 $data = $backup_sizes['full-orig'];
398
399                 if ( $parts['basename'] != $data['file'] ) {
400                         if ( defined('IMAGE_EDIT_OVERWRITE') && IMAGE_EDIT_OVERWRITE ) {
401                                 // delete only if it's edited image
402                                 if ( preg_match('/-e[0-9]{13}\./', $parts['basename']) ) {
403                                         $delpath = apply_filters('wp_delete_file', $file);
404                                         @unlink($delpath);
405                                 }
406                         } else {
407                                 $backup_sizes["full-$suffix"] = array('width' => $meta['width'], 'height' => $meta['height'], 'file' => $parts['basename']);
408                         }
409                 }
410
411                 $restored_file = path_join($parts['dirname'], $data['file']);
412                 $restored = update_attached_file($post_id, $restored_file);
413
414                 $meta['file'] = _wp_relative_upload_path( $restored_file );
415                 $meta['width'] = $data['width'];
416                 $meta['height'] = $data['height'];
417                 list ( $uwidth, $uheight ) = wp_constrain_dimensions($meta['width'], $meta['height'], 128, 96);
418                 $meta['hwstring_small'] = "height='$uheight' width='$uwidth'";
419         }
420
421         foreach ( $default_sizes as $default_size ) {
422                 if ( isset($backup_sizes["$default_size-orig"]) ) {
423                         $data = $backup_sizes["$default_size-orig"];
424                         if ( isset($meta['sizes'][$default_size]) && $meta['sizes'][$default_size]['file'] != $data['file'] ) {
425                                 if ( defined('IMAGE_EDIT_OVERWRITE') && IMAGE_EDIT_OVERWRITE ) {
426                                         // delete only if it's edited image
427                                         if ( preg_match('/-e[0-9]{13}-/', $meta['sizes'][$default_size]['file']) ) {
428                                                 $delpath = apply_filters( 'wp_delete_file', path_join($parts['dirname'], $meta['sizes'][$default_size]['file']) );
429                                                 @unlink($delpath);
430                                         }
431                                 } else {
432                                         $backup_sizes["$default_size-{$suffix}"] = $meta['sizes'][$default_size];
433                                 }
434                         }
435
436                         $meta['sizes'][$default_size] = $data;
437                 } else {
438                         unset($meta['sizes'][$default_size]);
439                 }
440         }
441
442         if ( !wp_update_attachment_metadata($post_id, $meta) || !update_post_meta( $post_id, '_wp_attachment_backup_sizes', $backup_sizes) ) {
443                 $msg->error = __('Cannot save image metadata.');
444                 return $msg;
445         }
446
447         if ( !$restored )
448                 $msg->error = __('Image metadata is inconsistent.');
449         else
450                 $msg->msg = __('Image restored successfully.');
451
452         return $msg;
453 }
454
455 function wp_save_image($post_id) {
456         $return = new stdClass;
457         $success = $delete = $scaled = $nocrop = false;
458         $post = get_post($post_id);
459         @ini_set( 'memory_limit', apply_filters( 'admin_memory_limit', WP_MAX_MEMORY_LIMIT ) );
460         $img = load_image_to_edit($post_id, $post->post_mime_type);
461
462         if ( !is_resource($img) ) {
463                 $return->error = esc_js( __('Unable to create new image.') );
464                 return $return;
465         }
466
467         $fwidth = !empty($_REQUEST['fwidth']) ? intval($_REQUEST['fwidth']) : 0;
468         $fheight = !empty($_REQUEST['fheight']) ? intval($_REQUEST['fheight']) : 0;
469         $target = !empty($_REQUEST['target']) ? preg_replace('/[^a-z0-9_-]+/i', '', $_REQUEST['target']) : '';
470         $scale = !empty($_REQUEST['do']) && 'scale' == $_REQUEST['do'];
471
472         if ( $scale && $fwidth > 0 && $fheight > 0 ) {
473                 $sX = imagesx($img);
474                 $sY = imagesy($img);
475
476                 // check if it has roughly the same w / h ratio
477                 $diff = round($sX / $sY, 2) - round($fwidth / $fheight, 2);
478                 if ( -0.1 < $diff && $diff < 0.1 ) {
479                         // scale the full size image
480                         $dst = wp_imagecreatetruecolor($fwidth, $fheight);
481                         if ( imagecopyresampled( $dst, $img, 0, 0, 0, 0, $fwidth, $fheight, $sX, $sY ) ) {
482                                 imagedestroy($img);
483                                 $img = $dst;
484                                 $scaled = true;
485                         }
486                 }
487
488                 if ( !$scaled ) {
489                         $return->error = esc_js( __('Error while saving the scaled image. Please reload the page and try again.') );
490                         return $return;
491                 }
492         } elseif ( !empty($_REQUEST['history']) ) {
493                 $changes = json_decode( stripslashes($_REQUEST['history']) );
494                 if ( $changes )
495                         $img = image_edit_apply_changes($img, $changes);
496         } else {
497                 $return->error = esc_js( __('Nothing to save, the image has not changed.') );
498                 return $return;
499         }
500
501         $meta = wp_get_attachment_metadata($post_id);
502         $backup_sizes = get_post_meta( $post->ID, '_wp_attachment_backup_sizes', true );
503
504         if ( !is_array($meta) ) {
505                 $return->error = esc_js( __('Image data does not exist. Please re-upload the image.') );
506                 return $return;
507         }
508
509         if ( !is_array($backup_sizes) )
510                 $backup_sizes = array();
511
512         // generate new filename
513         $path = get_attached_file($post_id);
514         $path_parts = pathinfo( $path );
515         $filename = $path_parts['filename'];
516         $suffix = time() . rand(100, 999);
517
518         if ( defined('IMAGE_EDIT_OVERWRITE') && IMAGE_EDIT_OVERWRITE &&
519                 isset($backup_sizes['full-orig']) && $backup_sizes['full-orig']['file'] != $path_parts['basename'] ) {
520
521                 if ( 'thumbnail' == $target )
522                         $new_path = "{$path_parts['dirname']}/{$filename}-temp.{$path_parts['extension']}";
523                 else
524                         $new_path = $path;
525         } else {
526                 while( true ) {
527                         $filename = preg_replace( '/-e([0-9]+)$/', '', $filename );
528                         $filename .= "-e{$suffix}";
529                         $new_filename = "{$filename}.{$path_parts['extension']}";
530                         $new_path = "{$path_parts['dirname']}/$new_filename";
531                         if ( file_exists($new_path) )
532                                 $suffix++;
533                         else
534                                 break;
535                 }
536         }
537
538         // save the full-size file, also needed to create sub-sizes
539         if ( !wp_save_image_file($new_path, $img, $post->post_mime_type, $post_id) ) {
540                 $return->error = esc_js( __('Unable to save the image.') );
541                 return $return;
542         }
543
544         if ( 'nothumb' == $target || 'all' == $target || 'full' == $target || $scaled ) {
545                 $tag = false;
546                 if ( isset($backup_sizes['full-orig']) ) {
547                         if ( ( !defined('IMAGE_EDIT_OVERWRITE') || !IMAGE_EDIT_OVERWRITE ) && $backup_sizes['full-orig']['file'] != $path_parts['basename'] )
548                                 $tag = "full-$suffix";
549                 } else {
550                         $tag = 'full-orig';
551                 }
552
553                 if ( $tag )
554                         $backup_sizes[$tag] = array('width' => $meta['width'], 'height' => $meta['height'], 'file' => $path_parts['basename']);
555
556                 $success = update_attached_file($post_id, $new_path);
557
558                 $meta['file'] = _wp_relative_upload_path($new_path);
559                 $meta['width'] = imagesx($img);
560                 $meta['height'] = imagesy($img);
561
562                 list ( $uwidth, $uheight ) = wp_constrain_dimensions($meta['width'], $meta['height'], 128, 96);
563                 $meta['hwstring_small'] = "height='$uheight' width='$uwidth'";
564
565                 if ( $success && ('nothumb' == $target || 'all' == $target) ) {
566                         $sizes = get_intermediate_image_sizes();
567                         if ( 'nothumb' == $target )
568                                 $sizes = array_diff( $sizes, array('thumbnail') );
569                 }
570
571                 $return->fw = $meta['width'];
572                 $return->fh = $meta['height'];
573         } elseif ( 'thumbnail' == $target ) {
574                 $sizes = array( 'thumbnail' );
575                 $success = $delete = $nocrop = true;
576         }
577
578         if ( isset($sizes) ) {
579                 foreach ( $sizes as $size ) {
580                         $tag = false;
581                         if ( isset($meta['sizes'][$size]) ) {
582                                 if ( isset($backup_sizes["$size-orig"]) ) {
583                                         if ( ( !defined('IMAGE_EDIT_OVERWRITE') || !IMAGE_EDIT_OVERWRITE ) && $backup_sizes["$size-orig"]['file'] != $meta['sizes'][$size]['file'] )
584                                                 $tag = "$size-$suffix";
585                                 } else {
586                                         $tag = "$size-orig";
587                                 }
588
589                                 if ( $tag )
590                                         $backup_sizes[$tag] = $meta['sizes'][$size];
591                         }
592
593                         $crop = $nocrop ? false : get_option("{$size}_crop");
594                         $resized = image_make_intermediate_size($new_path, get_option("{$size}_size_w"), get_option("{$size}_size_h"), $crop );
595
596                         if ( $resized )
597                                 $meta['sizes'][$size] = $resized;
598                         else
599                                 unset($meta['sizes'][$size]);
600                 }
601         }
602
603         if ( $success ) {
604                 wp_update_attachment_metadata($post_id, $meta);
605                 update_post_meta( $post_id, '_wp_attachment_backup_sizes', $backup_sizes);
606
607                 if ( $target == 'thumbnail' || $target == 'all' || $target == 'full' ) {
608                         $file_url = wp_get_attachment_url($post_id);
609                         if ( $thumb = $meta['sizes']['thumbnail'] )
610                                 $return->thumbnail = path_join( dirname($file_url), $thumb['file'] );
611                         else
612                                 $return->thumbnail = "$file_url?w=128&h=128";
613                 }
614         } else {
615                 $delete = true;
616         }
617
618         if ( $delete ) {
619                 $delpath = apply_filters('wp_delete_file', $new_path);
620                 @unlink($delpath);
621         }
622
623         imagedestroy($img);
624
625         $return->msg = esc_js( __('Image saved') );
626         return $return;
627 }