Wordpress 3.1
[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'); ?>?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 load_image_to_edit($post_id, $mime_type, $size = 'full') {
201         $filepath = get_attached_file($post_id);
202
203         if ( $filepath && file_exists($filepath) ) {
204                 if ( 'full' != $size && ( $data = image_get_intermediate_size($post_id, $size) ) ) {
205                         $filepath = apply_filters('load_image_to_edit_filesystempath', path_join( dirname($filepath), $data['file'] ), $post_id, $size);
206                 }
207         } elseif ( WP_Http_Fopen::test() ) {
208                 $filepath = apply_filters('load_image_to_edit_attachmenturl', wp_get_attachment_url($post_id) , $post_id, $size);
209         }
210
211         $filepath = apply_filters('load_image_to_edit_path', $filepath, $post_id, $size);
212         if ( empty($filepath) )
213                 return false;
214
215         switch ( $mime_type ) {
216                 case 'image/jpeg':
217                         $image = imagecreatefromjpeg($filepath);
218                         break;
219                 case 'image/png':
220                         $image = imagecreatefrompng($filepath);
221                         break;
222                 case 'image/gif':
223                         $image = imagecreatefromgif($filepath);
224                         break;
225                 default:
226                         $image = false;
227                         break;
228         }
229         if ( is_resource($image) ) {
230                 $image = apply_filters('load_image_to_edit', $image, $post_id, $size);
231                 if ( function_exists('imagealphablending') && function_exists('imagesavealpha') ) {
232                         imagealphablending($image, false);
233                         imagesavealpha($image, true);
234                 }
235         }
236         return $image;
237 }
238
239 function wp_stream_image($image, $mime_type, $post_id) {
240         $image = apply_filters('image_save_pre', $image, $post_id);
241
242         switch ( $mime_type ) {
243                 case 'image/jpeg':
244                         header('Content-Type: image/jpeg');
245                         return imagejpeg($image, null, 90);
246                 case 'image/png':
247                         header('Content-Type: image/png');
248                         return imagepng($image);
249                 case 'image/gif':
250                         header('Content-Type: image/gif');
251                         return imagegif($image);
252                 default:
253                         return false;
254         }
255 }
256
257 function wp_save_image_file($filename, $image, $mime_type, $post_id) {
258         $image = apply_filters('image_save_pre', $image, $post_id);
259         $saved = apply_filters('wp_save_image_file', null, $filename, $image, $mime_type, $post_id);
260         if ( null !== $saved )
261                 return $saved;
262
263         switch ( $mime_type ) {
264                 case 'image/jpeg':
265                         return imagejpeg( $image, $filename, apply_filters( 'jpeg_quality', 90, 'edit_image' ) );
266                 case 'image/png':
267                         return imagepng($image, $filename);
268                 case 'image/gif':
269                         return imagegif($image, $filename);
270                 default:
271                         return false;
272         }
273 }
274
275 function _image_get_preview_ratio($w, $h) {
276         $max = max($w, $h);
277         return $max > 400 ? (400 / $max) : 1;
278 }
279
280 function _rotate_image_resource($img, $angle) {
281         if ( function_exists('imagerotate') ) {
282                 $rotated = imagerotate($img, $angle, 0);
283                 if ( is_resource($rotated) ) {
284                         imagedestroy($img);
285                         $img = $rotated;
286                 }
287         }
288         return $img;
289 }
290
291
292 function _flip_image_resource($img, $horz, $vert) {
293         $w = imagesx($img);
294         $h = imagesy($img);
295         $dst = wp_imagecreatetruecolor($w, $h);
296         if ( is_resource($dst) ) {
297                 $sx = $vert ? ($w - 1) : 0;
298                 $sy = $horz ? ($h - 1) : 0;
299                 $sw = $vert ? -$w : $w;
300                 $sh = $horz ? -$h : $h;
301
302                 if ( imagecopyresampled($dst, $img, 0, 0, $sx, $sy, $w, $h, $sw, $sh) ) {
303                         imagedestroy($img);
304                         $img = $dst;
305                 }
306         }
307         return $img;
308 }
309
310 function _crop_image_resource($img, $x, $y, $w, $h) {
311         $dst = wp_imagecreatetruecolor($w, $h);
312         if ( is_resource($dst) ) {
313                 if ( imagecopy($dst, $img, 0, 0, $x, $y, $w, $h) ) {
314                         imagedestroy($img);
315                         $img = $dst;
316                 }
317         }
318         return $img;
319 }
320
321 function image_edit_apply_changes($img, $changes) {
322
323         if ( !is_array($changes) )
324                 return $img;
325
326         // expand change operations
327         foreach ( $changes as $key => $obj ) {
328                 if ( isset($obj->r) ) {
329                         $obj->type = 'rotate';
330                         $obj->angle = $obj->r;
331                         unset($obj->r);
332                 } elseif ( isset($obj->f) ) {
333                         $obj->type = 'flip';
334                         $obj->axis = $obj->f;
335                         unset($obj->f);
336                 } elseif ( isset($obj->c) ) {
337                         $obj->type = 'crop';
338                         $obj->sel = $obj->c;
339                         unset($obj->c);
340                 }
341                 $changes[$key] = $obj;
342         }
343
344         // combine operations
345         if ( count($changes) > 1 ) {
346                 $filtered = array($changes[0]);
347                 for ( $i = 0, $j = 1; $j < count($changes); $j++ ) {
348                         $combined = false;
349                         if ( $filtered[$i]->type == $changes[$j]->type ) {
350                                 switch ( $filtered[$i]->type ) {
351                                         case 'rotate':
352                                                 $filtered[$i]->angle += $changes[$j]->angle;
353                                                 $combined = true;
354                                                 break;
355                                         case 'flip':
356                                                 $filtered[$i]->axis ^= $changes[$j]->axis;
357                                                 $combined = true;
358                                                 break;
359                                 }
360                         }
361                         if ( !$combined )
362                                 $filtered[++$i] = $changes[$j];
363                 }
364                 $changes = $filtered;
365                 unset($filtered);
366         }
367
368         // image resource before applying the changes
369         $img = apply_filters('image_edit_before_change', $img, $changes);
370
371         foreach ( $changes as $operation ) {
372                 switch ( $operation->type ) {
373                         case 'rotate':
374                                 if ( $operation->angle != 0 )
375                                         $img = _rotate_image_resource($img, $operation->angle);
376                                 break;
377                         case 'flip':
378                                 if ( $operation->axis != 0 )
379                                         $img = _flip_image_resource($img, ($operation->axis & 1) != 0, ($operation->axis & 2) != 0);
380                                 break;
381                         case 'crop':
382                                 $sel = $operation->sel;
383                                 $scale = 1 / _image_get_preview_ratio( imagesx($img), imagesy($img) ); // discard preview scaling
384                                 $img = _crop_image_resource($img, $sel->x * $scale, $sel->y * $scale, $sel->w * $scale, $sel->h * $scale);
385                                 break;
386                 }
387         }
388
389         return $img;
390 }
391
392 function stream_preview_image($post_id) {
393         $post = get_post($post_id);
394         @ini_set('memory_limit', '256M');
395         $img = load_image_to_edit( $post_id, $post->post_mime_type, array(400, 400) );
396
397         if ( !is_resource($img) )
398                 return false;
399
400         $changes = !empty($_REQUEST['history']) ? json_decode( stripslashes($_REQUEST['history']) ) : null;
401         if ( $changes )
402                 $img = image_edit_apply_changes($img, $changes);
403
404         // scale the image
405         $w = imagesx($img);
406         $h = imagesy($img);
407         $ratio = _image_get_preview_ratio($w, $h);
408         $w2 = $w * $ratio;
409         $h2 = $h * $ratio;
410
411         $preview = wp_imagecreatetruecolor($w2, $h2);
412         imagecopyresampled( $preview, $img, 0, 0, 0, 0, $w2, $h2, $w, $h );
413         wp_stream_image($preview, $post->post_mime_type, $post_id);
414
415         imagedestroy($preview);
416         imagedestroy($img);
417         return true;
418 }
419
420 function wp_restore_image($post_id) {
421         $meta = wp_get_attachment_metadata($post_id);
422         $file = get_attached_file($post_id);
423         $backup_sizes = get_post_meta( $post_id, '_wp_attachment_backup_sizes', true );
424         $restored = false;
425         $msg = new stdClass;
426
427         if ( !is_array($backup_sizes) ) {
428                 $msg->error = __('Cannot load image metadata.');
429                 return $msg;
430         }
431
432         $parts = pathinfo($file);
433         $suffix = time() . rand(100, 999);
434         $default_sizes = apply_filters( 'intermediate_image_sizes', array('large', 'medium', 'thumbnail') );
435
436         if ( isset($backup_sizes['full-orig']) && is_array($backup_sizes['full-orig']) ) {
437                 $data = $backup_sizes['full-orig'];
438
439                 if ( $parts['basename'] != $data['file'] ) {
440                         if ( defined('IMAGE_EDIT_OVERWRITE') && IMAGE_EDIT_OVERWRITE ) {
441                                 // delete only if it's edited image
442                                 if ( preg_match('/-e[0-9]{13}\./', $parts['basename']) ) {
443                                         $delpath = apply_filters('wp_delete_file', $file);
444                                         @unlink($delpath);
445                                 }
446                         } else {
447                                 $backup_sizes["full-$suffix"] = array('width' => $meta['width'], 'height' => $meta['height'], 'file' => $parts['basename']);
448                         }
449                 }
450
451                 $restored_file = path_join($parts['dirname'], $data['file']);
452                 $restored = update_attached_file($post_id, $restored_file);
453
454                 $meta['file'] = _wp_relative_upload_path( $restored_file );
455                 $meta['width'] = $data['width'];
456                 $meta['height'] = $data['height'];
457                 list ( $uwidth, $uheight ) = wp_constrain_dimensions($meta['width'], $meta['height'], 128, 96);
458                 $meta['hwstring_small'] = "height='$uheight' width='$uwidth'";
459         }
460
461         foreach ( $default_sizes as $default_size ) {
462                 if ( isset($backup_sizes["$default_size-orig"]) ) {
463                         $data = $backup_sizes["$default_size-orig"];
464                         if ( isset($meta['sizes'][$default_size]) && $meta['sizes'][$default_size]['file'] != $data['file'] ) {
465                                 if ( defined('IMAGE_EDIT_OVERWRITE') && IMAGE_EDIT_OVERWRITE ) {
466                                         // delete only if it's edited image
467                                         if ( preg_match('/-e[0-9]{13}-/', $meta['sizes'][$default_size]['file']) ) {
468                                                 $delpath = apply_filters( 'wp_delete_file', path_join($parts['dirname'], $meta['sizes'][$default_size]['file']) );
469                                                 @unlink($delpath);
470                                         }
471                                 } else {
472                                         $backup_sizes["$default_size-{$suffix}"] = $meta['sizes'][$default_size];
473                                 }
474                         }
475
476                         $meta['sizes'][$default_size] = $data;
477                 } else {
478                         unset($meta['sizes'][$default_size]);
479                 }
480         }
481
482         if ( !wp_update_attachment_metadata($post_id, $meta) || !update_post_meta( $post_id, '_wp_attachment_backup_sizes', $backup_sizes) ) {
483                 $msg->error = __('Cannot save image metadata.');
484                 return $msg;
485         }
486
487         if ( !$restored )
488                 $msg->error = __('Image metadata is inconsistent.');
489         else
490                 $msg->msg = __('Image restored successfully.');
491
492         return $msg;
493 }
494
495 function wp_save_image($post_id) {
496         $return = new stdClass;
497         $success = $delete = $scaled = $nocrop = false;
498         $post = get_post($post_id);
499         @ini_set('memory_limit', '256M');
500         $img = load_image_to_edit($post_id, $post->post_mime_type);
501
502         if ( !is_resource($img) ) {
503                 $return->error = esc_js( __('Unable to create new image.') );
504                 return $return;
505         }
506
507         $fwidth = !empty($_REQUEST['fwidth']) ? intval($_REQUEST['fwidth']) : 0;
508         $fheight = !empty($_REQUEST['fheight']) ? intval($_REQUEST['fheight']) : 0;
509         $target = !empty($_REQUEST['target']) ? preg_replace('/[^a-z0-9_-]+/i', '', $_REQUEST['target']) : '';
510         $scale = !empty($_REQUEST['do']) && 'scale' == $_REQUEST['do'];
511
512         if ( $scale && $fwidth > 0 && $fheight > 0 ) {
513                 $sX = imagesx($img);
514                 $sY = imagesy($img);
515
516                 // check if it has roughly the same w / h ratio
517                 $diff = round($sX / $sY, 2) - round($fwidth / $fheight, 2);
518                 if ( -0.1 < $diff && $diff < 0.1 ) {
519                         // scale the full size image
520                         $dst = wp_imagecreatetruecolor($fwidth, $fheight);
521                         if ( imagecopyresampled( $dst, $img, 0, 0, 0, 0, $fwidth, $fheight, $sX, $sY ) ) {
522                                 imagedestroy($img);
523                                 $img = $dst;
524                                 $scaled = true;
525                         }
526                 }
527
528                 if ( !$scaled ) {
529                         $return->error = esc_js( __('Error while saving the scaled image. Please reload the page and try again.') );
530                         return $return;
531                 }
532         } elseif ( !empty($_REQUEST['history']) ) {
533                 $changes = json_decode( stripslashes($_REQUEST['history']) );
534                 if ( $changes )
535                         $img = image_edit_apply_changes($img, $changes);
536         } else {
537                 $return->error = esc_js( __('Nothing to save, the image has not changed.') );
538                 return $return;
539         }
540
541         $meta = wp_get_attachment_metadata($post_id);
542         $backup_sizes = get_post_meta( $post->ID, '_wp_attachment_backup_sizes', true );
543
544         if ( !is_array($meta) ) {
545                 $return->error = esc_js( __('Image data does not exist. Please re-upload the image.') );
546                 return $return;
547         }
548
549         if ( !is_array($backup_sizes) )
550                 $backup_sizes = array();
551
552         // generate new filename
553         $path = get_attached_file($post_id);
554         $path_parts = pathinfo52( $path );
555         $filename = $path_parts['filename'];
556         $suffix = time() . rand(100, 999);
557
558         if ( defined('IMAGE_EDIT_OVERWRITE') && IMAGE_EDIT_OVERWRITE &&
559                 isset($backup_sizes['full-orig']) && $backup_sizes['full-orig']['file'] != $path_parts['basename'] ) {
560
561                 if ( 'thumbnail' == $target )
562                         $new_path = "{$path_parts['dirname']}/{$filename}-temp.{$path_parts['extension']}";
563                 else
564                         $new_path = $path;
565         } else {
566                 while( true ) {
567                         $filename = preg_replace( '/-e([0-9]+)$/', '', $filename );
568                         $filename .= "-e{$suffix}";
569                         $new_filename = "{$filename}.{$path_parts['extension']}";
570                         $new_path = "{$path_parts['dirname']}/$new_filename";
571                         if ( file_exists($new_path) )
572                                 $suffix++;
573                         else
574                                 break;
575                 }
576         }
577
578         // save the full-size file, also needed to create sub-sizes
579         if ( !wp_save_image_file($new_path, $img, $post->post_mime_type, $post_id) ) {
580                 $return->error = esc_js( __('Unable to save the image.') );
581                 return $return;
582         }
583
584         if ( 'nothumb' == $target || 'all' == $target || 'full' == $target || $scaled ) {
585                 $tag = false;
586                 if ( isset($backup_sizes['full-orig']) ) {
587                         if ( ( !defined('IMAGE_EDIT_OVERWRITE') || !IMAGE_EDIT_OVERWRITE ) && $backup_sizes['full-orig']['file'] != $path_parts['basename'] )
588                                 $tag = "full-$suffix";
589                 } else {
590                         $tag = 'full-orig';
591                 }
592
593                 if ( $tag )
594                         $backup_sizes[$tag] = array('width' => $meta['width'], 'height' => $meta['height'], 'file' => $path_parts['basename']);
595
596                 $success = update_attached_file($post_id, $new_path);
597
598                 $meta['file'] = _wp_relative_upload_path($new_path);
599                 $meta['width'] = imagesx($img);
600                 $meta['height'] = imagesy($img);
601
602                 list ( $uwidth, $uheight ) = wp_constrain_dimensions($meta['width'], $meta['height'], 128, 96);
603                 $meta['hwstring_small'] = "height='$uheight' width='$uwidth'";
604
605                 if ( $success && ('nothumb' == $target || 'all' == $target) ) {
606                         $sizes = apply_filters( 'intermediate_image_sizes', array('large', 'medium', 'thumbnail') );
607                         if ( 'nothumb' == $target )
608                                 $sizes = array_diff( $sizes, array('thumbnail') );
609                 }
610
611                 $return->fw = $meta['width'];
612                 $return->fh = $meta['height'];
613         } elseif ( 'thumbnail' == $target ) {
614                 $sizes = array( 'thumbnail' );
615                 $success = $delete = $nocrop = true;
616         }
617
618         if ( isset($sizes) ) {
619                 foreach ( $sizes as $size ) {
620                         $tag = false;
621                         if ( isset($meta['sizes'][$size]) ) {
622                                 if ( isset($backup_sizes["$size-orig"]) ) {
623                                         if ( ( !defined('IMAGE_EDIT_OVERWRITE') || !IMAGE_EDIT_OVERWRITE ) && $backup_sizes["$size-orig"]['file'] != $meta['sizes'][$size]['file'] )
624                                                 $tag = "$size-$suffix";
625                                 } else {
626                                         $tag = "$size-orig";
627                                 }
628
629                                 if ( $tag )
630                                         $backup_sizes[$tag] = $meta['sizes'][$size];
631                         }
632
633                         $crop = $nocrop ? false : get_option("{$size}_crop");
634                         $resized = image_make_intermediate_size($new_path, get_option("{$size}_size_w"), get_option("{$size}_size_h"), $crop );
635
636                         if ( $resized )
637                                 $meta['sizes'][$size] = $resized;
638                         else
639                                 unset($meta['sizes'][$size]);
640                 }
641         }
642
643         if ( $success ) {
644                 wp_update_attachment_metadata($post_id, $meta);
645                 update_post_meta( $post_id, '_wp_attachment_backup_sizes', $backup_sizes);
646
647                 if ( $target == 'thumbnail' || $target == 'all' || $target == 'full' ) {
648                         $file_url = wp_get_attachment_url($post_id);
649                         if ( $thumb = $meta['sizes']['thumbnail'] )
650                                 $return->thumbnail = path_join( dirname($file_url), $thumb['file'] );
651                         else
652                                 $return->thumbnail = "$file_url?w=128&h=128";
653                 }
654         } else {
655                 $delete = true;
656         }
657
658         if ( $delete ) {
659                 $delpath = apply_filters('wp_delete_file', $new_path);
660                 @unlink($delpath);
661         }
662
663         imagedestroy($img);
664
665         $return->msg = esc_js( __('Image saved') );
666         return $return;
667 }
668