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