wp/wp-admin/includes/image-edit.php
changeset 0 d970ebf37754
child 5 5e2f62d02dcd
equal deleted inserted replaced
-1:000000000000 0:d970ebf37754
       
     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 ( isset( $meta['width'], $meta['height'] ) )
       
    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 = false;
       
    25 	if ( ! empty( $backup_sizes ) && isset( $backup_sizes['full-orig'], $meta['file'] ) )
       
    26 		$can_restore = $backup_sizes['full-orig']['file'] != basename( $meta['file'] );
       
    27 
       
    28 	if ( $msg ) {
       
    29 		if ( isset($msg->error) )
       
    30 			$note = "<div class='error'><p>$msg->error</p></div>";
       
    31 		elseif ( isset($msg->msg) )
       
    32 			$note = "<div class='updated'><p>$msg->msg</p></div>";
       
    33 	}
       
    34 
       
    35 	?>
       
    36 	<div class="imgedit-wrap">
       
    37 	<?php echo $note; ?>
       
    38 	<table id="imgedit-panel-<?php echo $post_id; ?>"><tbody>
       
    39 	<tr><td>
       
    40 	<div class="imgedit-menu">
       
    41 		<div onclick="imageEdit.crop(<?php echo "$post_id, '$nonce'"; ?>, this)" class="imgedit-crop disabled" title="<?php esc_attr_e( 'Crop' ); ?>"></div><?php
       
    42 
       
    43 	// On some setups GD library does not provide imagerotate() - Ticket #11536
       
    44 	if ( wp_image_editor_supports( array( 'mime_type' => get_post_mime_type( $post_id ), 'methods' => array( 'rotate' ) ) ) ) { ?>
       
    45 		<div class="imgedit-rleft"  onclick="imageEdit.rotate( 90, <?php echo "$post_id, '$nonce'"; ?>, this)" title="<?php esc_attr_e( 'Rotate counter-clockwise' ); ?>"></div>
       
    46 		<div class="imgedit-rright" onclick="imageEdit.rotate(-90, <?php echo "$post_id, '$nonce'"; ?>, this)" title="<?php esc_attr_e( 'Rotate clockwise' ); ?>"></div>
       
    47 <?php } else {
       
    48 		$note_no_rotate = esc_attr__('Image rotation is not supported by your web host.');
       
    49 ?>
       
    50 	    <div class="imgedit-rleft disabled"  title="<?php echo $note_no_rotate; ?>"></div>
       
    51 	    <div class="imgedit-rright disabled" title="<?php echo $note_no_rotate; ?>"></div>
       
    52 <?php } ?>
       
    53 
       
    54 		<div onclick="imageEdit.flip(1, <?php echo "$post_id, '$nonce'"; ?>, this)" class="imgedit-flipv" title="<?php esc_attr_e( 'Flip vertically' ); ?>"></div>
       
    55 		<div onclick="imageEdit.flip(2, <?php echo "$post_id, '$nonce'"; ?>, this)" class="imgedit-fliph" title="<?php esc_attr_e( 'Flip horizontally' ); ?>"></div>
       
    56 
       
    57 		<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>
       
    58 		<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>
       
    59 		<br class="clear" />
       
    60 	</div>
       
    61 
       
    62 	<input type="hidden" id="imgedit-sizer-<?php echo $post_id; ?>" value="<?php echo $sizer; ?>" />
       
    63 	<input type="hidden" id="imgedit-minthumb-<?php echo $post_id; ?>" value="<?php echo ( get_option('thumbnail_size_w') . ':' . get_option('thumbnail_size_h') ); ?>" />
       
    64 	<input type="hidden" id="imgedit-history-<?php echo $post_id; ?>" value="" />
       
    65 	<input type="hidden" id="imgedit-undone-<?php echo $post_id; ?>" value="0" />
       
    66 	<input type="hidden" id="imgedit-selection-<?php echo $post_id; ?>" value="" />
       
    67 	<input type="hidden" id="imgedit-x-<?php echo $post_id; ?>" value="<?php echo isset( $meta['width'] ) ? $meta['width'] : 0; ?>" />
       
    68 	<input type="hidden" id="imgedit-y-<?php echo $post_id; ?>" value="<?php echo isset( $meta['height'] ) ? $meta['height'] : 0; ?>" />
       
    69 
       
    70 	<div id="imgedit-crop-<?php echo $post_id; ?>" class="imgedit-crop-wrap">
       
    71 	<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); ?>" />
       
    72 	</div>
       
    73 
       
    74 	<div class="imgedit-submit">
       
    75 		<input type="button" onclick="imageEdit.close(<?php echo $post_id; ?>, 1)" class="button" value="<?php esc_attr_e( 'Cancel' ); ?>" />
       
    76 		<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' ); ?>" />
       
    77 	</div>
       
    78 	</td>
       
    79 
       
    80 	<td class="imgedit-settings">
       
    81 	<div class="imgedit-group">
       
    82 	<div class="imgedit-group-top">
       
    83 		<a class="imgedit-help-toggle" onclick="imageEdit.toggleHelp(this);return false;" href="#"><strong><?php _e('Scale Image'); ?></strong></a>
       
    84 		<div class="imgedit-help">
       
    85 		<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 images can only be scaled down, not up.'); ?></p>
       
    86 		<?php if ( isset( $meta['width'], $meta['height'] ) ): ?>
       
    87 		<p><?php printf( __('Original dimensions %s'), $meta['width'] . '&times;' . $meta['height'] ); ?></p>
       
    88 		<?php endif ?>
       
    89 		<div class="imgedit-submit">
       
    90 		<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 isset( $meta['width'] ) ? $meta['width'] : 0; ?>" />&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 isset( $meta['height'] ) ? $meta['height'] : 0; ?>" />
       
    91 		<span class="imgedit-scale-warn" id="imgedit-scale-warn-<?php echo $post_id; ?>">!</span></span>
       
    92 		<input type="button" onclick="imageEdit.action(<?php echo "$post_id, '$nonce'"; ?>, 'scale')" class="button-primary" value="<?php esc_attr_e( 'Scale' ); ?>" />
       
    93 		</div>
       
    94 		</div>
       
    95 	</div>
       
    96 
       
    97 <?php if ( $can_restore ) { ?>
       
    98 
       
    99 	<div class="imgedit-group-top">
       
   100 		<a class="imgedit-help-toggle" onclick="imageEdit.toggleHelp(this);return false;" href="#"><strong><?php _e('Restore Original Image'); ?></strong></a>
       
   101 		<div class="imgedit-help">
       
   102 		<p><?php _e('Discard any changes and restore the original image.');
       
   103 
       
   104 		if ( !defined('IMAGE_EDIT_OVERWRITE') || !IMAGE_EDIT_OVERWRITE )
       
   105 			echo ' '.__('Previously edited copies of the image will not be deleted.');
       
   106 
       
   107 		?></p>
       
   108 		<div class="imgedit-submit">
       
   109 		<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; ?> />
       
   110 		</div>
       
   111 		</div>
       
   112 	</div>
       
   113 
       
   114 <?php } ?>
       
   115 
       
   116 	</div>
       
   117 
       
   118 	<div class="imgedit-group">
       
   119 	<div class="imgedit-group-top">
       
   120 		<strong><?php _e('Image Crop'); ?></strong>
       
   121 		<a class="imgedit-help-toggle" onclick="imageEdit.toggleHelp(this);return false;" href="#"><?php _e('(help)'); ?></a>
       
   122 		<div class="imgedit-help">
       
   123 		<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>
       
   124 
       
   125 		<p><strong><?php _e('Crop Aspect Ratio'); ?></strong><br />
       
   126 		<?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>
       
   127 
       
   128 		<p><strong><?php _e('Crop Selection'); ?></strong><br />
       
   129 		<?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>
       
   130 		</div>
       
   131 	</div>
       
   132 
       
   133 	<p>
       
   134 		<?php _e('Aspect ratio:'); ?>
       
   135 		<span  class="nowrap">
       
   136 		<input type="text" id="imgedit-crop-width-<?php echo $post_id; ?>" onkeyup="imageEdit.setRatioSelection(<?php echo $post_id; ?>, 0, this)" style="width:3em;" />
       
   137 		:
       
   138 		<input type="text" id="imgedit-crop-height-<?php echo $post_id; ?>" onkeyup="imageEdit.setRatioSelection(<?php echo $post_id; ?>, 1, this)" style="width:3em;" />
       
   139 		</span>
       
   140 	</p>
       
   141 
       
   142 	<p id="imgedit-crop-sel-<?php echo $post_id; ?>">
       
   143 		<?php _e('Selection:'); ?>
       
   144 		<span  class="nowrap">
       
   145 		<input type="text" id="imgedit-sel-width-<?php echo $post_id; ?>" onkeyup="imageEdit.setNumSelection(<?php echo $post_id; ?>)" style="width:4em;" />
       
   146 		:
       
   147 		<input type="text" id="imgedit-sel-height-<?php echo $post_id; ?>" onkeyup="imageEdit.setNumSelection(<?php echo $post_id; ?>)" style="width:4em;" />
       
   148 		</span>
       
   149 	</p>
       
   150 	</div>
       
   151 
       
   152 	<?php if ( $thumb && $sub_sizes ) {
       
   153 		$thumb_img = wp_constrain_dimensions( $thumb['width'], $thumb['height'], 160, 120 );
       
   154 	?>
       
   155 
       
   156 	<div class="imgedit-group imgedit-applyto">
       
   157 	<div class="imgedit-group-top">
       
   158 		<strong><?php _e('Thumbnail Settings'); ?></strong>
       
   159 		<a class="imgedit-help-toggle" onclick="imageEdit.toggleHelp(this);return false;" href="#"><?php _e('(help)'); ?></a>
       
   160 		<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>
       
   161 	</div>
       
   162 
       
   163 	<p>
       
   164 		<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'); ?>
       
   165 	</p>
       
   166 
       
   167 	<p id="imgedit-save-target-<?php echo $post_id; ?>">
       
   168 		<strong><?php _e('Apply changes to:'); ?></strong><br />
       
   169 
       
   170 		<label class="imgedit-label">
       
   171 		<input type="radio" name="imgedit-target-<?php echo $post_id; ?>" value="all" checked="checked" />
       
   172 		<?php _e('All image sizes'); ?></label>
       
   173 
       
   174 		<label class="imgedit-label">
       
   175 		<input type="radio" name="imgedit-target-<?php echo $post_id; ?>" value="thumbnail" />
       
   176 		<?php _e('Thumbnail'); ?></label>
       
   177 
       
   178 		<label class="imgedit-label">
       
   179 		<input type="radio" name="imgedit-target-<?php echo $post_id; ?>" value="nothumb" />
       
   180 		<?php _e('All sizes except thumbnail'); ?></label>
       
   181 	</p>
       
   182 	</div>
       
   183 
       
   184 	<?php } ?>
       
   185 
       
   186 	</td></tr>
       
   187 	</tbody></table>
       
   188 	<div class="imgedit-wait" id="imgedit-wait-<?php echo $post_id; ?>"></div>
       
   189 	<script type="text/javascript">jQuery( function() { imageEdit.init(<?php echo $post_id; ?>); });</script>
       
   190 	<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>
       
   191 	</div>
       
   192 <?php
       
   193 }
       
   194 
       
   195 /**
       
   196  * Streams image in WP_Image_Editor to browser.
       
   197  * Provided for backcompat reasons
       
   198  *
       
   199  * @param WP_Image_Editor $image
       
   200  * @param string $mime_type
       
   201  * @param int $post_id
       
   202  * @return boolean
       
   203  */
       
   204 function wp_stream_image( $image, $mime_type, $post_id ) {
       
   205 	if ( $image instanceof WP_Image_Editor ) {
       
   206 		$image = apply_filters('image_editor_save_pre', $image, $post_id);
       
   207 
       
   208 		if ( is_wp_error( $image->stream( $mime_type ) ) )
       
   209 			return false;
       
   210 
       
   211 		return true;
       
   212 	} else {
       
   213 		_deprecated_argument( __FUNCTION__, '3.5', __( '$image needs to be an WP_Image_Editor object' ) );
       
   214 
       
   215 		$image = apply_filters('image_save_pre', $image, $post_id);
       
   216 
       
   217 		switch ( $mime_type ) {
       
   218 			case 'image/jpeg':
       
   219 				header( 'Content-Type: image/jpeg' );
       
   220 				return imagejpeg( $image, null, 90 );
       
   221 			case 'image/png':
       
   222 				header( 'Content-Type: image/png' );
       
   223 				return imagepng( $image );
       
   224 			case 'image/gif':
       
   225 				header( 'Content-Type: image/gif' );
       
   226 				return imagegif( $image );
       
   227 			default:
       
   228 				return false;
       
   229 		}
       
   230 	}
       
   231 }
       
   232 
       
   233 /**
       
   234  * Saves Image to File
       
   235  *
       
   236  * @param string $filename
       
   237  * @param WP_Image_Editor $image
       
   238  * @param string $mime_type
       
   239  * @param int $post_id
       
   240  * @return boolean
       
   241  */
       
   242 function wp_save_image_file( $filename, $image, $mime_type, $post_id ) {
       
   243 	if ( $image instanceof WP_Image_Editor ) {
       
   244 		$image = apply_filters('image_editor_save_pre', $image, $post_id);
       
   245 		$saved = apply_filters('wp_save_image_editor_file', null, $filename, $image, $mime_type, $post_id);
       
   246 
       
   247 		if ( null !== $saved )
       
   248 			return $saved;
       
   249 
       
   250 		return $image->save( $filename, $mime_type );
       
   251 	} else {
       
   252 		_deprecated_argument( __FUNCTION__, '3.5', __( '$image needs to be an WP_Image_Editor object' ) );
       
   253 
       
   254 		$image = apply_filters('image_save_pre', $image, $post_id);
       
   255 		$saved = apply_filters('wp_save_image_file', null, $filename, $image, $mime_type, $post_id);
       
   256 
       
   257 		if ( null !== $saved )
       
   258 			return $saved;
       
   259 
       
   260 		switch ( $mime_type ) {
       
   261 			case 'image/jpeg':
       
   262 				return imagejpeg( $image, $filename, apply_filters( 'jpeg_quality', 90, 'edit_image' ) );
       
   263 			case 'image/png':
       
   264 				return imagepng( $image, $filename );
       
   265 			case 'image/gif':
       
   266 				return imagegif( $image, $filename );
       
   267 			default:
       
   268 				return false;
       
   269 		}
       
   270 	}
       
   271 }
       
   272 
       
   273 function _image_get_preview_ratio($w, $h) {
       
   274 	$max = max($w, $h);
       
   275 	return $max > 400 ? (400 / $max) : 1;
       
   276 }
       
   277 
       
   278 // @TODO: Returns GD resource, but is NOT public
       
   279 function _rotate_image_resource($img, $angle) {
       
   280 	_deprecated_function( __FUNCTION__, '3.5', __( 'Use WP_Image_Editor::rotate' ) );
       
   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  * @TODO: Only used within image_edit_apply_changes
       
   293  *		  and receives/returns GD Resource.
       
   294  *		  Consider removal.
       
   295  *
       
   296  * @param GD_Resource $img
       
   297  * @param boolean $horz
       
   298  * @param boolean $vert
       
   299  * @return GD_Resource
       
   300  */
       
   301 function _flip_image_resource($img, $horz, $vert) {
       
   302 	_deprecated_function( __FUNCTION__, '3.5', __( 'Use WP_Image_Editor::flip' ) );
       
   303 	$w = imagesx($img);
       
   304 	$h = imagesy($img);
       
   305 	$dst = wp_imagecreatetruecolor($w, $h);
       
   306 	if ( is_resource($dst) ) {
       
   307 		$sx = $vert ? ($w - 1) : 0;
       
   308 		$sy = $horz ? ($h - 1) : 0;
       
   309 		$sw = $vert ? -$w : $w;
       
   310 		$sh = $horz ? -$h : $h;
       
   311 
       
   312 		if ( imagecopyresampled($dst, $img, 0, 0, $sx, $sy, $w, $h, $sw, $sh) ) {
       
   313 			imagedestroy($img);
       
   314 			$img = $dst;
       
   315 		}
       
   316 	}
       
   317 	return $img;
       
   318 }
       
   319 
       
   320 /**
       
   321  * @TODO: Only used within image_edit_apply_changes
       
   322  *		  and receives/returns GD Resource.
       
   323  *		  Consider removal.
       
   324  *
       
   325  * @param GD_Resource $img
       
   326  * @param float $x
       
   327  * @param float $y
       
   328  * @param float $w
       
   329  * @param float $h
       
   330  * @return GD_Resource
       
   331  */
       
   332 function _crop_image_resource($img, $x, $y, $w, $h) {
       
   333 	$dst = wp_imagecreatetruecolor($w, $h);
       
   334 	if ( is_resource($dst) ) {
       
   335 		if ( imagecopy($dst, $img, 0, 0, $x, $y, $w, $h) ) {
       
   336 			imagedestroy($img);
       
   337 			$img = $dst;
       
   338 		}
       
   339 	}
       
   340 	return $img;
       
   341 }
       
   342 
       
   343 /**
       
   344  * Performs group of changes on Editor specified.
       
   345  *
       
   346  * @param WP_Image_Editor $image
       
   347  * @param type $changes
       
   348  * @return WP_Image_Editor
       
   349  */
       
   350 function image_edit_apply_changes( $image, $changes ) {
       
   351 	if ( is_resource( $image ) )
       
   352 		_deprecated_argument( __FUNCTION__, '3.5', __( '$image needs to be an WP_Image_Editor object' ) );
       
   353 
       
   354 	if ( !is_array($changes) )
       
   355 		return $image;
       
   356 
       
   357 	// expand change operations
       
   358 	foreach ( $changes as $key => $obj ) {
       
   359 		if ( isset($obj->r) ) {
       
   360 			$obj->type = 'rotate';
       
   361 			$obj->angle = $obj->r;
       
   362 			unset($obj->r);
       
   363 		} elseif ( isset($obj->f) ) {
       
   364 			$obj->type = 'flip';
       
   365 			$obj->axis = $obj->f;
       
   366 			unset($obj->f);
       
   367 		} elseif ( isset($obj->c) ) {
       
   368 			$obj->type = 'crop';
       
   369 			$obj->sel = $obj->c;
       
   370 			unset($obj->c);
       
   371 		}
       
   372 		$changes[$key] = $obj;
       
   373 	}
       
   374 
       
   375 	// combine operations
       
   376 	if ( count($changes) > 1 ) {
       
   377 		$filtered = array($changes[0]);
       
   378 		for ( $i = 0, $j = 1; $j < count($changes); $j++ ) {
       
   379 			$combined = false;
       
   380 			if ( $filtered[$i]->type == $changes[$j]->type ) {
       
   381 				switch ( $filtered[$i]->type ) {
       
   382 					case 'rotate':
       
   383 						$filtered[$i]->angle += $changes[$j]->angle;
       
   384 						$combined = true;
       
   385 						break;
       
   386 					case 'flip':
       
   387 						$filtered[$i]->axis ^= $changes[$j]->axis;
       
   388 						$combined = true;
       
   389 						break;
       
   390 				}
       
   391 			}
       
   392 			if ( !$combined )
       
   393 				$filtered[++$i] = $changes[$j];
       
   394 		}
       
   395 		$changes = $filtered;
       
   396 		unset($filtered);
       
   397 	}
       
   398 
       
   399 	// image resource before applying the changes
       
   400 	if ( $image instanceof WP_Image_Editor )
       
   401 		$image = apply_filters('wp_image_editor_before_change', $image, $changes);
       
   402 	elseif ( is_resource( $image ) )
       
   403 		$image = apply_filters('image_edit_before_change', $image, $changes);
       
   404 
       
   405 	foreach ( $changes as $operation ) {
       
   406 		switch ( $operation->type ) {
       
   407 			case 'rotate':
       
   408 				if ( $operation->angle != 0 ) {
       
   409 					if ( $image instanceof WP_Image_Editor )
       
   410 						$image->rotate( $operation->angle );
       
   411 					else
       
   412 						$image = _rotate_image_resource( $image, $operation->angle );
       
   413 				}
       
   414 				break;
       
   415 			case 'flip':
       
   416 				if ( $operation->axis != 0 )
       
   417 					if ( $image instanceof WP_Image_Editor )
       
   418 						$image->flip( ($operation->axis & 1) != 0, ($operation->axis & 2) != 0 );
       
   419 					else
       
   420 						$image = _flip_image_resource( $image, ( $operation->axis & 1 ) != 0, ( $operation->axis & 2 ) != 0 );
       
   421 				break;
       
   422 			case 'crop':
       
   423 				$sel = $operation->sel;
       
   424 
       
   425 				if ( $image instanceof WP_Image_Editor ) {
       
   426 					$size = $image->get_size();
       
   427 					$w = $size['width'];
       
   428 					$h = $size['height'];
       
   429 
       
   430 					$scale = 1 / _image_get_preview_ratio( $w, $h ); // discard preview scaling
       
   431 					$image->crop( $sel->x * $scale, $sel->y * $scale, $sel->w * $scale, $sel->h * $scale );
       
   432 				} else {
       
   433 					$scale = 1 / _image_get_preview_ratio( imagesx( $image ), imagesy( $image ) ); // discard preview scaling
       
   434 					$image = _crop_image_resource( $image, $sel->x * $scale, $sel->y * $scale, $sel->w * $scale, $sel->h * $scale );
       
   435 				}
       
   436 				break;
       
   437 		}
       
   438 	}
       
   439 
       
   440 	return $image;
       
   441 }
       
   442 
       
   443 
       
   444 /**
       
   445  * Streams image in post to browser, along with enqueued changes
       
   446  * in $_REQUEST['history']
       
   447  *
       
   448  * @param int $post_id
       
   449  * @return boolean
       
   450  */
       
   451 function stream_preview_image( $post_id ) {
       
   452 	$post = get_post( $post_id );
       
   453 	@ini_set( 'memory_limit', apply_filters( 'admin_memory_limit', WP_MAX_MEMORY_LIMIT ) );
       
   454 
       
   455 	$img = wp_get_image_editor( _load_image_to_edit_path( $post_id ) );
       
   456 
       
   457     if ( is_wp_error( $img ) )
       
   458         return false;
       
   459 
       
   460 	$changes = !empty($_REQUEST['history']) ? json_decode( wp_unslash($_REQUEST['history']) ) : null;
       
   461 	if ( $changes )
       
   462 		$img = image_edit_apply_changes( $img, $changes );
       
   463 
       
   464 	// scale the image
       
   465 	$size = $img->get_size();
       
   466 	$w = $size['width'];
       
   467 	$h = $size['height'];
       
   468 
       
   469 	$ratio = _image_get_preview_ratio( $w, $h );
       
   470 	$w2 = max ( 1, $w * $ratio );
       
   471 	$h2 = max ( 1, $h * $ratio );
       
   472 
       
   473 	if ( is_wp_error( $img->resize( $w2, $h2 ) ) )
       
   474 		return false;
       
   475 
       
   476 	return wp_stream_image( $img, $post->post_mime_type, $post_id );
       
   477 }
       
   478 
       
   479 function wp_restore_image($post_id) {
       
   480 	$meta = wp_get_attachment_metadata($post_id);
       
   481 	$file = get_attached_file($post_id);
       
   482 	$backup_sizes = get_post_meta( $post_id, '_wp_attachment_backup_sizes', true );
       
   483 	$restored = false;
       
   484 	$msg = new stdClass;
       
   485 
       
   486 	if ( !is_array($backup_sizes) ) {
       
   487 		$msg->error = __('Cannot load image metadata.');
       
   488 		return $msg;
       
   489 	}
       
   490 
       
   491 	$parts = pathinfo($file);
       
   492 	$suffix = time() . rand(100, 999);
       
   493 	$default_sizes = get_intermediate_image_sizes();
       
   494 
       
   495 	if ( isset($backup_sizes['full-orig']) && is_array($backup_sizes['full-orig']) ) {
       
   496 		$data = $backup_sizes['full-orig'];
       
   497 
       
   498 		if ( $parts['basename'] != $data['file'] ) {
       
   499 			if ( defined('IMAGE_EDIT_OVERWRITE') && IMAGE_EDIT_OVERWRITE ) {
       
   500 				// delete only if it's edited image
       
   501 				if ( preg_match('/-e[0-9]{13}\./', $parts['basename']) ) {
       
   502 					/** This filter is documented in wp-admin/custom-header.php */
       
   503 					$delpath = apply_filters('wp_delete_file', $file);
       
   504 					@unlink($delpath);
       
   505 				}
       
   506 			} elseif ( isset( $meta['width'], $meta['height'] ) ) {
       
   507 				$backup_sizes["full-$suffix"] = array('width' => $meta['width'], 'height' => $meta['height'], 'file' => $parts['basename']);
       
   508 			}
       
   509 		}
       
   510 
       
   511 		$restored_file = path_join($parts['dirname'], $data['file']);
       
   512 		$restored = update_attached_file($post_id, $restored_file);
       
   513 
       
   514 		$meta['file'] = _wp_relative_upload_path( $restored_file );
       
   515 		$meta['width'] = $data['width'];
       
   516 		$meta['height'] = $data['height'];
       
   517 	}
       
   518 
       
   519 	foreach ( $default_sizes as $default_size ) {
       
   520 		if ( isset($backup_sizes["$default_size-orig"]) ) {
       
   521 			$data = $backup_sizes["$default_size-orig"];
       
   522 			if ( isset($meta['sizes'][$default_size]) && $meta['sizes'][$default_size]['file'] != $data['file'] ) {
       
   523 				if ( defined('IMAGE_EDIT_OVERWRITE') && IMAGE_EDIT_OVERWRITE ) {
       
   524 					// delete only if it's edited image
       
   525 					if ( preg_match('/-e[0-9]{13}-/', $meta['sizes'][$default_size]['file']) ) {
       
   526 						/** This filter is documented in wp-admin/custom-header.php */
       
   527 						$delpath = apply_filters( 'wp_delete_file', path_join($parts['dirname'], $meta['sizes'][$default_size]['file']) );
       
   528 						@unlink($delpath);
       
   529 					}
       
   530 				} else {
       
   531 					$backup_sizes["$default_size-{$suffix}"] = $meta['sizes'][$default_size];
       
   532 				}
       
   533 			}
       
   534 
       
   535 			$meta['sizes'][$default_size] = $data;
       
   536 		} else {
       
   537 			unset($meta['sizes'][$default_size]);
       
   538 		}
       
   539 	}
       
   540 
       
   541 	if ( !wp_update_attachment_metadata($post_id, $meta) || !update_post_meta( $post_id, '_wp_attachment_backup_sizes', $backup_sizes) ) {
       
   542 		$msg->error = __('Cannot save image metadata.');
       
   543 		return $msg;
       
   544 	}
       
   545 
       
   546 	if ( !$restored )
       
   547 		$msg->error = __('Image metadata is inconsistent.');
       
   548 	else
       
   549 		$msg->msg = __('Image restored successfully.');
       
   550 
       
   551 	return $msg;
       
   552 }
       
   553 
       
   554 /**
       
   555  * Saves image to post along with enqueued changes
       
   556  * in $_REQUEST['history']
       
   557  *
       
   558  * @param int $post_id
       
   559  * @return \stdClass
       
   560  */
       
   561 function wp_save_image( $post_id ) {
       
   562 	$return = new stdClass;
       
   563 	$success = $delete = $scaled = $nocrop = false;
       
   564 	$post = get_post( $post_id );
       
   565 
       
   566 	$img = wp_get_image_editor( _load_image_to_edit_path( $post_id, 'full' ) );
       
   567 	if ( is_wp_error( $img ) ) {
       
   568 		$return->error = esc_js( __('Unable to create new image.') );
       
   569 		return $return;
       
   570 	}
       
   571 
       
   572 	$fwidth = !empty($_REQUEST['fwidth']) ? intval($_REQUEST['fwidth']) : 0;
       
   573 	$fheight = !empty($_REQUEST['fheight']) ? intval($_REQUEST['fheight']) : 0;
       
   574 	$target = !empty($_REQUEST['target']) ? preg_replace('/[^a-z0-9_-]+/i', '', $_REQUEST['target']) : '';
       
   575 	$scale = !empty($_REQUEST['do']) && 'scale' == $_REQUEST['do'];
       
   576 
       
   577 	if ( $scale && $fwidth > 0 && $fheight > 0 ) {
       
   578 		$size = $img->get_size();
       
   579 		$sX = $size['width'];
       
   580 		$sY = $size['height'];
       
   581 
       
   582 		// check if it has roughly the same w / h ratio
       
   583 		$diff = round($sX / $sY, 2) - round($fwidth / $fheight, 2);
       
   584 		if ( -0.1 < $diff && $diff < 0.1 ) {
       
   585 			// scale the full size image
       
   586 			if ( $img->resize( $fwidth, $fheight ) )
       
   587 				$scaled = true;
       
   588 		}
       
   589 
       
   590 		if ( !$scaled ) {
       
   591 			$return->error = esc_js( __('Error while saving the scaled image. Please reload the page and try again.') );
       
   592 			return $return;
       
   593 		}
       
   594 	} elseif ( !empty($_REQUEST['history']) ) {
       
   595 		$changes = json_decode( wp_unslash($_REQUEST['history']) );
       
   596 		if ( $changes )
       
   597 			$img = image_edit_apply_changes($img, $changes);
       
   598 	} else {
       
   599 		$return->error = esc_js( __('Nothing to save, the image has not changed.') );
       
   600 		return $return;
       
   601 	}
       
   602 
       
   603 	$meta = wp_get_attachment_metadata($post_id);
       
   604 	$backup_sizes = get_post_meta( $post->ID, '_wp_attachment_backup_sizes', true );
       
   605 
       
   606 	if ( !is_array($meta) ) {
       
   607 		$return->error = esc_js( __('Image data does not exist. Please re-upload the image.') );
       
   608 		return $return;
       
   609 	}
       
   610 
       
   611 	if ( !is_array($backup_sizes) )
       
   612 		$backup_sizes = array();
       
   613 
       
   614 	// generate new filename
       
   615 	$path = get_attached_file($post_id);
       
   616 	$path_parts = pathinfo( $path );
       
   617 	$filename = $path_parts['filename'];
       
   618 	$suffix = time() . rand(100, 999);
       
   619 
       
   620 	if ( defined('IMAGE_EDIT_OVERWRITE') && IMAGE_EDIT_OVERWRITE &&
       
   621 		isset($backup_sizes['full-orig']) && $backup_sizes['full-orig']['file'] != $path_parts['basename'] ) {
       
   622 
       
   623 		if ( 'thumbnail' == $target )
       
   624 			$new_path = "{$path_parts['dirname']}/{$filename}-temp.{$path_parts['extension']}";
       
   625 		else
       
   626 			$new_path = $path;
       
   627 	} else {
       
   628 		while( true ) {
       
   629 			$filename = preg_replace( '/-e([0-9]+)$/', '', $filename );
       
   630 			$filename .= "-e{$suffix}";
       
   631 			$new_filename = "{$filename}.{$path_parts['extension']}";
       
   632 			$new_path = "{$path_parts['dirname']}/$new_filename";
       
   633 			if ( file_exists($new_path) )
       
   634 				$suffix++;
       
   635 			else
       
   636 				break;
       
   637 		}
       
   638 	}
       
   639 
       
   640 	// save the full-size file, also needed to create sub-sizes
       
   641 	if ( !wp_save_image_file($new_path, $img, $post->post_mime_type, $post_id) ) {
       
   642 		$return->error = esc_js( __('Unable to save the image.') );
       
   643 		return $return;
       
   644 	}
       
   645 
       
   646 	if ( 'nothumb' == $target || 'all' == $target || 'full' == $target || $scaled ) {
       
   647 		$tag = false;
       
   648 		if ( isset($backup_sizes['full-orig']) ) {
       
   649 			if ( ( !defined('IMAGE_EDIT_OVERWRITE') || !IMAGE_EDIT_OVERWRITE ) && $backup_sizes['full-orig']['file'] != $path_parts['basename'] )
       
   650 				$tag = "full-$suffix";
       
   651 		} else {
       
   652 			$tag = 'full-orig';
       
   653 		}
       
   654 
       
   655 		if ( $tag )
       
   656 			$backup_sizes[$tag] = array('width' => $meta['width'], 'height' => $meta['height'], 'file' => $path_parts['basename']);
       
   657 
       
   658 		$success = update_attached_file( $post_id, $new_path );
       
   659 
       
   660 		$meta['file'] = _wp_relative_upload_path( $new_path );
       
   661 
       
   662 		$size = $img->get_size();
       
   663 		$meta['width'] = $size['width'];
       
   664 		$meta['height'] = $size['height'];
       
   665 
       
   666 		if ( $success && ('nothumb' == $target || 'all' == $target) ) {
       
   667 			$sizes = get_intermediate_image_sizes();
       
   668 			if ( 'nothumb' == $target )
       
   669 				$sizes = array_diff( $sizes, array('thumbnail') );
       
   670 		}
       
   671 
       
   672 		$return->fw = $meta['width'];
       
   673 		$return->fh = $meta['height'];
       
   674 	} elseif ( 'thumbnail' == $target ) {
       
   675 		$sizes = array( 'thumbnail' );
       
   676 		$success = $delete = $nocrop = true;
       
   677 	}
       
   678 
       
   679 	if ( isset( $sizes ) ) {
       
   680 		$_sizes = array();
       
   681 
       
   682 		foreach ( $sizes as $size ) {
       
   683 			$tag = false;
       
   684 			if ( isset( $meta['sizes'][$size] ) ) {
       
   685 				if ( isset($backup_sizes["$size-orig"]) ) {
       
   686 					if ( ( !defined('IMAGE_EDIT_OVERWRITE') || !IMAGE_EDIT_OVERWRITE ) && $backup_sizes["$size-orig"]['file'] != $meta['sizes'][$size]['file'] )
       
   687 						$tag = "$size-$suffix";
       
   688 				} else {
       
   689 					$tag = "$size-orig";
       
   690 				}
       
   691 
       
   692 				if ( $tag )
       
   693 					$backup_sizes[$tag] = $meta['sizes'][$size];
       
   694 			}
       
   695 
       
   696 			$crop = $nocrop ? false : get_option("{$size}_crop");
       
   697 			$_sizes[ $size ] = array( 'width' => get_option("{$size}_size_w"), 'height' => get_option("{$size}_size_h"), 'crop' => $crop );
       
   698 		}
       
   699 
       
   700 		$meta['sizes'] = array_merge( $meta['sizes'], $img->multi_resize( $_sizes ) );
       
   701 	}
       
   702 
       
   703 	unset( $img );
       
   704 
       
   705 	if ( $success ) {
       
   706 		wp_update_attachment_metadata( $post_id, $meta );
       
   707 		update_post_meta( $post_id, '_wp_attachment_backup_sizes', $backup_sizes);
       
   708 
       
   709 		if ( $target == 'thumbnail' || $target == 'all' || $target == 'full' ) {
       
   710 			// Check if it's an image edit from attachment edit screen
       
   711 			if ( ! empty( $_REQUEST['context'] ) && 'edit-attachment' == $_REQUEST['context'] ) {
       
   712 				$thumb_url = wp_get_attachment_image_src( $post_id, array( 900, 600 ), true );
       
   713 				$return->thumbnail = $thumb_url[0];
       
   714 			} else {
       
   715 				$file_url = wp_get_attachment_url($post_id);
       
   716 				if ( $thumb = $meta['sizes']['thumbnail'] )
       
   717 					$return->thumbnail = path_join( dirname($file_url), $thumb['file'] );
       
   718 				else
       
   719 					$return->thumbnail = "$file_url?w=128&h=128";
       
   720 			}
       
   721 		}
       
   722 	} else {
       
   723 		$delete = true;
       
   724 	}
       
   725 
       
   726 	if ( $delete ) {
       
   727 		/** This filter is documented in wp-admin/custom-header.php */
       
   728 		$delpath = apply_filters('wp_delete_file', $new_path);
       
   729 		@unlink( $delpath );
       
   730 	}
       
   731 
       
   732 	$return->msg = esc_js( __('Image saved') );
       
   733 	return $return;
       
   734 }