ru-se.com

Unnamed repository; edit this file 'description' to name the repository.
Log | Files | Refs

image-edit.php (36423B)


      1 <?php
      2 /**
      3  * WordPress Image Editor
      4  *
      5  * @package WordPress
      6  * @subpackage Administration
      7  */
      8 
      9 /**
     10  * Loads the WP image-editing interface.
     11  *
     12  * @since 2.9.0
     13  *
     14  * @param int          $post_id Attachment post ID.
     15  * @param false|object $msg     Optional. Message to display for image editor updates or errors.
     16  *                              Default false.
     17  */
     18 function wp_image_editor( $post_id, $msg = false ) {
     19 	$nonce     = wp_create_nonce( "image_editor-$post_id" );
     20 	$meta      = wp_get_attachment_metadata( $post_id );
     21 	$thumb     = image_get_intermediate_size( $post_id, 'thumbnail' );
     22 	$sub_sizes = isset( $meta['sizes'] ) && is_array( $meta['sizes'] );
     23 	$note      = '';
     24 
     25 	if ( isset( $meta['width'], $meta['height'] ) ) {
     26 		$big = max( $meta['width'], $meta['height'] );
     27 	} else {
     28 		die( __( 'Image data does not exist. Please re-upload the image.' ) );
     29 	}
     30 
     31 	$sizer = $big > 400 ? 400 / $big : 1;
     32 
     33 	$backup_sizes = get_post_meta( $post_id, '_wp_attachment_backup_sizes', true );
     34 	$can_restore  = false;
     35 	if ( ! empty( $backup_sizes ) && isset( $backup_sizes['full-orig'], $meta['file'] ) ) {
     36 		$can_restore = wp_basename( $meta['file'] ) !== $backup_sizes['full-orig']['file'];
     37 	}
     38 
     39 	if ( $msg ) {
     40 		if ( isset( $msg->error ) ) {
     41 			$note = "<div class='notice notice-error' tabindex='-1' role='alert'><p>$msg->error</p></div>";
     42 		} elseif ( isset( $msg->msg ) ) {
     43 			$note = "<div class='notice notice-success' tabindex='-1' role='alert'><p>$msg->msg</p></div>";
     44 		}
     45 	}
     46 
     47 	?>
     48 	<div class="imgedit-wrap wp-clearfix">
     49 	<div id="imgedit-panel-<?php echo $post_id; ?>">
     50 
     51 	<div class="imgedit-panel-content wp-clearfix">
     52 		<?php echo $note; ?>
     53 		<div class="imgedit-menu wp-clearfix">
     54 			<button type="button" onclick="imageEdit.handleCropToolClick( <?php echo "$post_id, '$nonce'"; ?>, this )" class="imgedit-crop button disabled" disabled><?php esc_html_e( 'Crop' ); ?></button>
     55 			<?php
     56 
     57 			// On some setups GD library does not provide imagerotate() - Ticket #11536.
     58 			if ( wp_image_editor_supports(
     59 				array(
     60 					'mime_type' => get_post_mime_type( $post_id ),
     61 					'methods'   => array( 'rotate' ),
     62 				)
     63 			) ) {
     64 				$note_no_rotate = '';
     65 				?>
     66 				<button type="button" class="imgedit-rleft button" onclick="imageEdit.rotate( 90, <?php echo "$post_id, '$nonce'"; ?>, this)"><?php esc_html_e( 'Rotate left' ); ?></button>
     67 				<button type="button" class="imgedit-rright button" onclick="imageEdit.rotate(-90, <?php echo "$post_id, '$nonce'"; ?>, this)"><?php esc_html_e( 'Rotate right' ); ?></button>
     68 				<?php
     69 			} else {
     70 				$note_no_rotate = '<p class="note-no-rotate"><em>' . __( 'Image rotation is not supported by your web host.' ) . '</em></p>';
     71 				?>
     72 				<button type="button" class="imgedit-rleft button disabled" disabled></button>
     73 				<button type="button" class="imgedit-rright button disabled" disabled></button>
     74 			<?php } ?>
     75 
     76 			<button type="button" onclick="imageEdit.flip(1, <?php echo "$post_id, '$nonce'"; ?>, this)" class="imgedit-flipv button"><?php esc_html_e( 'Flip vertical' ); ?></button>
     77 			<button type="button" onclick="imageEdit.flip(2, <?php echo "$post_id, '$nonce'"; ?>, this)" class="imgedit-fliph button"><?php esc_html_e( 'Flip horizontal' ); ?></button>
     78 
     79 			<br class="imgedit-undo-redo-separator" />
     80 			<button type="button" id="image-undo-<?php echo $post_id; ?>" onclick="imageEdit.undo(<?php echo "$post_id, '$nonce'"; ?>, this)" class="imgedit-undo button disabled" disabled><?php esc_html_e( 'Undo' ); ?></button>
     81 			<button type="button" id="image-redo-<?php echo $post_id; ?>" onclick="imageEdit.redo(<?php echo "$post_id, '$nonce'"; ?>, this)" class="imgedit-redo button disabled" disabled><?php esc_html_e( 'Redo' ); ?></button>
     82 			<?php echo $note_no_rotate; ?>
     83 		</div>
     84 
     85 		<input type="hidden" id="imgedit-sizer-<?php echo $post_id; ?>" value="<?php echo $sizer; ?>" />
     86 		<input type="hidden" id="imgedit-history-<?php echo $post_id; ?>" value="" />
     87 		<input type="hidden" id="imgedit-undone-<?php echo $post_id; ?>" value="0" />
     88 		<input type="hidden" id="imgedit-selection-<?php echo $post_id; ?>" value="" />
     89 		<input type="hidden" id="imgedit-x-<?php echo $post_id; ?>" value="<?php echo isset( $meta['width'] ) ? $meta['width'] : 0; ?>" />
     90 		<input type="hidden" id="imgedit-y-<?php echo $post_id; ?>" value="<?php echo isset( $meta['height'] ) ? $meta['height'] : 0; ?>" />
     91 
     92 		<div id="imgedit-crop-<?php echo $post_id; ?>" class="imgedit-crop-wrap">
     93 		<img id="image-preview-<?php echo $post_id; ?>" onload="imageEdit.imgLoaded('<?php echo $post_id; ?>')"
     94 			src="<?php echo esc_url( admin_url( 'admin-ajax.php', 'relative' ) ) . '?action=imgedit-preview&amp;_ajax_nonce=' . $nonce . '&amp;postid=' . $post_id . '&amp;rand=' . rand( 1, 99999 ); ?>" alt="" />
     95 		</div>
     96 
     97 		<div class="imgedit-submit">
     98 			<input type="button" onclick="imageEdit.close(<?php echo $post_id; ?>, 1)" class="button imgedit-cancel-btn" value="<?php esc_attr_e( 'Cancel' ); ?>" />
     99 			<input type="button" onclick="imageEdit.save(<?php echo "$post_id, '$nonce'"; ?>)" disabled="disabled" class="button button-primary imgedit-submit-btn" value="<?php esc_attr_e( 'Save' ); ?>" />
    100 		</div>
    101 	</div>
    102 
    103 	<div class="imgedit-settings">
    104 	<div class="imgedit-group">
    105 	<div class="imgedit-group-top">
    106 		<h2><?php _e( 'Scale Image' ); ?></h2>
    107 		<button type="button" class="dashicons dashicons-editor-help imgedit-help-toggle" onclick="imageEdit.toggleHelp(this);" aria-expanded="false"><span class="screen-reader-text"><?php esc_html_e( 'Scale Image Help' ); ?></span></button>
    108 		<div class="imgedit-help">
    109 		<p><?php _e( 'You can proportionally scale the original image. For best results, scaling should be done before you crop, flip, or rotate. Images can only be scaled down, not up.' ); ?></p>
    110 		</div>
    111 		<?php if ( isset( $meta['width'], $meta['height'] ) ) : ?>
    112 		<p>
    113 			<?php
    114 			printf(
    115 				/* translators: %s: Image width and height in pixels. */
    116 				__( 'Original dimensions %s' ),
    117 				'<span class="imgedit-original-dimensions">' . $meta['width'] . ' &times; ' . $meta['height'] . '</span>'
    118 			);
    119 			?>
    120 		</p>
    121 		<?php endif; ?>
    122 		<div class="imgedit-submit">
    123 
    124 		<fieldset class="imgedit-scale">
    125 		<legend><?php _e( 'New dimensions:' ); ?></legend>
    126 		<div class="nowrap">
    127 		<label for="imgedit-scale-width-<?php echo $post_id; ?>" class="screen-reader-text"><?php _e( 'scale width' ); ?></label>
    128 		<input type="text" id="imgedit-scale-width-<?php echo $post_id; ?>" onkeyup="imageEdit.scaleChanged(<?php echo $post_id; ?>, 1, this)" onblur="imageEdit.scaleChanged(<?php echo $post_id; ?>, 1, this)" value="<?php echo isset( $meta['width'] ) ? $meta['width'] : 0; ?>" />
    129 		<span class="imgedit-separator" aria-hidden="true">&times;</span>
    130 		<label for="imgedit-scale-height-<?php echo $post_id; ?>" class="screen-reader-text"><?php _e( 'scale height' ); ?></label>
    131 		<input type="text" id="imgedit-scale-height-<?php echo $post_id; ?>" onkeyup="imageEdit.scaleChanged(<?php echo $post_id; ?>, 0, this)" onblur="imageEdit.scaleChanged(<?php echo $post_id; ?>, 0, this)" value="<?php echo isset( $meta['height'] ) ? $meta['height'] : 0; ?>" />
    132 		<span class="imgedit-scale-warn" id="imgedit-scale-warn-<?php echo $post_id; ?>">!</span>
    133 		<div class="imgedit-scale-button-wrapper"><input id="imgedit-scale-button" type="button" onclick="imageEdit.action(<?php echo "$post_id, '$nonce'"; ?>, 'scale')" class="button button-primary" value="<?php esc_attr_e( 'Scale' ); ?>" /></div>
    134 		</div>
    135 		</fieldset>
    136 
    137 		</div>
    138 	</div>
    139 	</div>
    140 
    141 	<?php if ( $can_restore ) { ?>
    142 
    143 	<div class="imgedit-group">
    144 	<div class="imgedit-group-top">
    145 		<h2><button type="button" onclick="imageEdit.toggleHelp(this);" class="button-link" aria-expanded="false"><?php _e( 'Restore original image' ); ?> <span class="dashicons dashicons-arrow-down imgedit-help-toggle"></span></button></h2>
    146 		<div class="imgedit-help imgedit-restore">
    147 		<p>
    148 			<?php
    149 			_e( 'Discard any changes and restore the original image.' );
    150 
    151 			if ( ! defined( 'IMAGE_EDIT_OVERWRITE' ) || ! IMAGE_EDIT_OVERWRITE ) {
    152 				echo ' ' . __( 'Previously edited copies of the image will not be deleted.' );
    153 			}
    154 			?>
    155 		</p>
    156 		<div class="imgedit-submit">
    157 		<input type="button" onclick="imageEdit.action(<?php echo "$post_id, '$nonce'"; ?>, 'restore')" class="button button-primary" value="<?php esc_attr_e( 'Restore image' ); ?>" <?php echo $can_restore; ?> />
    158 		</div>
    159 		</div>
    160 	</div>
    161 	</div>
    162 
    163 	<?php } ?>
    164 
    165 	<div class="imgedit-group">
    166 	<div class="imgedit-group-top">
    167 		<h2><?php _e( 'Image Crop' ); ?></h2>
    168 		<button type="button" class="dashicons dashicons-editor-help imgedit-help-toggle" onclick="imageEdit.toggleHelp(this);" aria-expanded="false"><span class="screen-reader-text"><?php esc_html_e( 'Image Crop Help' ); ?></span></button>
    169 
    170 		<div class="imgedit-help">
    171 		<p><?php _e( 'To crop the image, click on it and drag to make your selection.' ); ?></p>
    172 
    173 		<p><strong><?php _e( 'Crop Aspect Ratio' ); ?></strong><br />
    174 		<?php _e( 'The aspect ratio is the relationship between the width and height. You can preserve the aspect ratio by holding down the shift key while resizing your selection. Use the input box to specify the aspect ratio, e.g. 1:1 (square), 4:3, 16:9, etc.' ); ?></p>
    175 
    176 		<p><strong><?php _e( 'Crop Selection' ); ?></strong><br />
    177 		<?php _e( 'Once you have made your selection, you can adjust it by entering the size in pixels. The minimum selection size is the thumbnail size as set in the Media settings.' ); ?></p>
    178 		</div>
    179 	</div>
    180 
    181 	<fieldset class="imgedit-crop-ratio">
    182 		<legend><?php _e( 'Aspect ratio:' ); ?></legend>
    183 		<div class="nowrap">
    184 		<label for="imgedit-crop-width-<?php echo $post_id; ?>" class="screen-reader-text"><?php _e( 'crop ratio width' ); ?></label>
    185 		<input type="text" id="imgedit-crop-width-<?php echo $post_id; ?>" onkeyup="imageEdit.setRatioSelection(<?php echo $post_id; ?>, 0, this)" onblur="imageEdit.setRatioSelection(<?php echo $post_id; ?>, 0, this)" />
    186 		<span class="imgedit-separator" aria-hidden="true">:</span>
    187 		<label for="imgedit-crop-height-<?php echo $post_id; ?>" class="screen-reader-text"><?php _e( 'crop ratio height' ); ?></label>
    188 		<input type="text" id="imgedit-crop-height-<?php echo $post_id; ?>" onkeyup="imageEdit.setRatioSelection(<?php echo $post_id; ?>, 1, this)" onblur="imageEdit.setRatioSelection(<?php echo $post_id; ?>, 1, this)" />
    189 		</div>
    190 	</fieldset>
    191 
    192 	<fieldset id="imgedit-crop-sel-<?php echo $post_id; ?>" class="imgedit-crop-sel">
    193 		<legend><?php _e( 'Selection:' ); ?></legend>
    194 		<div class="nowrap">
    195 		<label for="imgedit-sel-width-<?php echo $post_id; ?>" class="screen-reader-text"><?php _e( 'selection width' ); ?></label>
    196 		<input type="text" id="imgedit-sel-width-<?php echo $post_id; ?>" onkeyup="imageEdit.setNumSelection(<?php echo $post_id; ?>, this)" onblur="imageEdit.setNumSelection(<?php echo $post_id; ?>, this)" />
    197 		<span class="imgedit-separator" aria-hidden="true">&times;</span>
    198 		<label for="imgedit-sel-height-<?php echo $post_id; ?>" class="screen-reader-text"><?php _e( 'selection height' ); ?></label>
    199 		<input type="text" id="imgedit-sel-height-<?php echo $post_id; ?>" onkeyup="imageEdit.setNumSelection(<?php echo $post_id; ?>, this)" onblur="imageEdit.setNumSelection(<?php echo $post_id; ?>, this)" />
    200 		</div>
    201 	</fieldset>
    202 
    203 	</div>
    204 
    205 	<?php
    206 	if ( $thumb && $sub_sizes ) {
    207 		$thumb_img = wp_constrain_dimensions( $thumb['width'], $thumb['height'], 160, 120 );
    208 		?>
    209 
    210 	<div class="imgedit-group imgedit-applyto">
    211 	<div class="imgedit-group-top">
    212 		<h2><?php _e( 'Thumbnail Settings' ); ?></h2>
    213 		<button type="button" class="dashicons dashicons-editor-help imgedit-help-toggle" onclick="imageEdit.toggleHelp(this);" aria-expanded="false"><span class="screen-reader-text"><?php esc_html_e( 'Thumbnail Settings Help' ); ?></span></button>
    214 		<div class="imgedit-help">
    215 		<p><?php _e( 'You can edit the image while preserving the thumbnail. For example, you may wish to have a square thumbnail that displays just a section of the image.' ); ?></p>
    216 		</div>
    217 	</div>
    218 
    219 	<figure class="imgedit-thumbnail-preview">
    220 		<img src="<?php echo $thumb['url']; ?>" width="<?php echo $thumb_img[0]; ?>" height="<?php echo $thumb_img[1]; ?>" class="imgedit-size-preview" alt="" draggable="false" />
    221 		<figcaption class="imgedit-thumbnail-preview-caption"><?php _e( 'Current thumbnail' ); ?></figcaption>
    222 	</figure>
    223 
    224 	<div id="imgedit-save-target-<?php echo $post_id; ?>" class="imgedit-save-target">
    225 	<fieldset>
    226 		<legend><?php _e( 'Apply changes to:' ); ?></legend>
    227 
    228 		<span class="imgedit-label">
    229 			<input type="radio" id="imgedit-target-all" name="imgedit-target-<?php echo $post_id; ?>" value="all" checked="checked" />
    230 			<label for="imgedit-target-all"><?php _e( 'All image sizes' ); ?></label>
    231 		</span>
    232 
    233 		<span class="imgedit-label">
    234 			<input type="radio" id="imgedit-target-thumbnail" name="imgedit-target-<?php echo $post_id; ?>" value="thumbnail" />
    235 			<label for="imgedit-target-thumbnail"><?php _e( 'Thumbnail' ); ?></label>
    236 		</span>
    237 
    238 		<span class="imgedit-label">
    239 			<input type="radio" id="imgedit-target-nothumb" name="imgedit-target-<?php echo $post_id; ?>" value="nothumb" />
    240 			<label for="imgedit-target-nothumb"><?php _e( 'All sizes except thumbnail' ); ?></label>
    241 		</span>
    242 	</fieldset>
    243 	</div>
    244 	</div>
    245 
    246 	<?php } ?>
    247 
    248 	</div>
    249 
    250 	</div>
    251 	<div class="imgedit-wait" id="imgedit-wait-<?php echo $post_id; ?>"></div>
    252 	<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>
    253 	</div>
    254 	<?php
    255 }
    256 
    257 /**
    258  * Streams image in WP_Image_Editor to browser.
    259  *
    260  * @since 2.9.0
    261  *
    262  * @param WP_Image_Editor $image         The image editor instance.
    263  * @param string          $mime_type     The mime type of the image.
    264  * @param int             $attachment_id The image's attachment post ID.
    265  * @return bool True on success, false on failure.
    266  */
    267 function wp_stream_image( $image, $mime_type, $attachment_id ) {
    268 	if ( $image instanceof WP_Image_Editor ) {
    269 
    270 		/**
    271 		 * Filters the WP_Image_Editor instance for the image to be streamed to the browser.
    272 		 *
    273 		 * @since 3.5.0
    274 		 *
    275 		 * @param WP_Image_Editor $image         The image editor instance.
    276 		 * @param int             $attachment_id The attachment post ID.
    277 		 */
    278 		$image = apply_filters( 'image_editor_save_pre', $image, $attachment_id );
    279 
    280 		if ( is_wp_error( $image->stream( $mime_type ) ) ) {
    281 			return false;
    282 		}
    283 
    284 		return true;
    285 	} else {
    286 		/* translators: 1: $image, 2: WP_Image_Editor */
    287 		_deprecated_argument( __FUNCTION__, '3.5.0', sprintf( __( '%1$s needs to be a %2$s object.' ), '$image', 'WP_Image_Editor' ) );
    288 
    289 		/**
    290 		 * Filters the GD image resource to be streamed to the browser.
    291 		 *
    292 		 * @since 2.9.0
    293 		 * @deprecated 3.5.0 Use {@see 'image_editor_save_pre'} instead.
    294 		 *
    295 		 * @param resource|GdImage $image         Image resource to be streamed.
    296 		 * @param int              $attachment_id The attachment post ID.
    297 		 */
    298 		$image = apply_filters_deprecated( 'image_save_pre', array( $image, $attachment_id ), '3.5.0', 'image_editor_save_pre' );
    299 
    300 		switch ( $mime_type ) {
    301 			case 'image/jpeg':
    302 				header( 'Content-Type: image/jpeg' );
    303 				return imagejpeg( $image, null, 90 );
    304 			case 'image/png':
    305 				header( 'Content-Type: image/png' );
    306 				return imagepng( $image );
    307 			case 'image/gif':
    308 				header( 'Content-Type: image/gif' );
    309 				return imagegif( $image );
    310 			case 'image/webp':
    311 				if ( function_exists( 'imagewebp' ) ) {
    312 					header( 'Content-Type: image/webp' );
    313 					return imagewebp( $image, null, 90 );
    314 				}
    315 				return false;
    316 			default:
    317 				return false;
    318 		}
    319 	}
    320 }
    321 
    322 /**
    323  * Saves image to file.
    324  *
    325  * @since 2.9.0
    326  *
    327  * @param string          $filename  Name of the file to be saved.
    328  * @param WP_Image_Editor $image     The image editor instance.
    329  * @param string          $mime_type The mime type of the image.
    330  * @param int             $post_id   Attachment post ID.
    331  * @return bool True on success, false on failure.
    332  */
    333 function wp_save_image_file( $filename, $image, $mime_type, $post_id ) {
    334 	if ( $image instanceof WP_Image_Editor ) {
    335 
    336 		/** This filter is documented in wp-admin/includes/image-edit.php */
    337 		$image = apply_filters( 'image_editor_save_pre', $image, $post_id );
    338 
    339 		/**
    340 		 * Filters whether to skip saving the image file.
    341 		 *
    342 		 * Returning a non-null value will short-circuit the save method,
    343 		 * returning that value instead.
    344 		 *
    345 		 * @since 3.5.0
    346 		 *
    347 		 * @param bool|null       $override  Value to return instead of saving. Default null.
    348 		 * @param string          $filename  Name of the file to be saved.
    349 		 * @param WP_Image_Editor $image     The image editor instance.
    350 		 * @param string          $mime_type The mime type of the image.
    351 		 * @param int             $post_id   Attachment post ID.
    352 		 */
    353 		$saved = apply_filters( 'wp_save_image_editor_file', null, $filename, $image, $mime_type, $post_id );
    354 
    355 		if ( null !== $saved ) {
    356 			return $saved;
    357 		}
    358 
    359 		return $image->save( $filename, $mime_type );
    360 	} else {
    361 		/* translators: 1: $image, 2: WP_Image_Editor */
    362 		_deprecated_argument( __FUNCTION__, '3.5.0', sprintf( __( '%1$s needs to be a %2$s object.' ), '$image', 'WP_Image_Editor' ) );
    363 
    364 		/** This filter is documented in wp-admin/includes/image-edit.php */
    365 		$image = apply_filters_deprecated( 'image_save_pre', array( $image, $post_id ), '3.5.0', 'image_editor_save_pre' );
    366 
    367 		/**
    368 		 * Filters whether to skip saving the image file.
    369 		 *
    370 		 * Returning a non-null value will short-circuit the save method,
    371 		 * returning that value instead.
    372 		 *
    373 		 * @since 2.9.0
    374 		 * @deprecated 3.5.0 Use {@see 'wp_save_image_editor_file'} instead.
    375 		 *
    376 		 * @param mixed           $override  Value to return instead of saving. Default null.
    377 		 * @param string          $filename  Name of the file to be saved.
    378 		 * @param WP_Image_Editor $image     The image editor instance.
    379 		 * @param string          $mime_type The mime type of the image.
    380 		 * @param int             $post_id   Attachment post ID.
    381 		 */
    382 		$saved = apply_filters_deprecated(
    383 			'wp_save_image_file',
    384 			array( null, $filename, $image, $mime_type, $post_id ),
    385 			'3.5.0',
    386 			'wp_save_image_editor_file'
    387 		);
    388 
    389 		if ( null !== $saved ) {
    390 			return $saved;
    391 		}
    392 
    393 		switch ( $mime_type ) {
    394 			case 'image/jpeg':
    395 				/** This filter is documented in wp-includes/class-wp-image-editor.php */
    396 				return imagejpeg( $image, $filename, apply_filters( 'jpeg_quality', 90, 'edit_image' ) );
    397 			case 'image/png':
    398 				return imagepng( $image, $filename );
    399 			case 'image/gif':
    400 				return imagegif( $image, $filename );
    401 			case 'image/webp':
    402 				if ( function_exists( 'imagewebp' ) ) {
    403 					return imagewebp( $image, $filename );
    404 				}
    405 				return false;
    406 			default:
    407 				return false;
    408 		}
    409 	}
    410 }
    411 
    412 /**
    413  * Image preview ratio. Internal use only.
    414  *
    415  * @since 2.9.0
    416  *
    417  * @ignore
    418  * @param int $w Image width in pixels.
    419  * @param int $h Image height in pixels.
    420  * @return float|int Image preview ratio.
    421  */
    422 function _image_get_preview_ratio( $w, $h ) {
    423 	$max = max( $w, $h );
    424 	return $max > 400 ? ( 400 / $max ) : 1;
    425 }
    426 
    427 /**
    428  * Returns an image resource. Internal use only.
    429  *
    430  * @since 2.9.0
    431  * @deprecated 3.5.0 Use WP_Image_Editor::rotate()
    432  * @see WP_Image_Editor::rotate()
    433  *
    434  * @ignore
    435  * @param resource|GdImage  $img   Image resource.
    436  * @param float|int         $angle Image rotation angle, in degrees.
    437  * @return resource|GdImage|false GD image resource or GdImage instance, false otherwise.
    438  */
    439 function _rotate_image_resource( $img, $angle ) {
    440 	_deprecated_function( __FUNCTION__, '3.5.0', 'WP_Image_Editor::rotate()' );
    441 
    442 	if ( function_exists( 'imagerotate' ) ) {
    443 		$rotated = imagerotate( $img, $angle, 0 );
    444 
    445 		if ( is_gd_image( $rotated ) ) {
    446 			imagedestroy( $img );
    447 			$img = $rotated;
    448 		}
    449 	}
    450 
    451 	return $img;
    452 }
    453 
    454 /**
    455  * Flips an image resource. Internal use only.
    456  *
    457  * @since 2.9.0
    458  * @deprecated 3.5.0 Use WP_Image_Editor::flip()
    459  * @see WP_Image_Editor::flip()
    460  *
    461  * @ignore
    462  * @param resource|GdImage $img  Image resource or GdImage instance.
    463  * @param bool             $horz Whether to flip horizontally.
    464  * @param bool             $vert Whether to flip vertically.
    465  * @return resource|GdImage (maybe) flipped image resource or GdImage instance.
    466  */
    467 function _flip_image_resource( $img, $horz, $vert ) {
    468 	_deprecated_function( __FUNCTION__, '3.5.0', 'WP_Image_Editor::flip()' );
    469 
    470 	$w   = imagesx( $img );
    471 	$h   = imagesy( $img );
    472 	$dst = wp_imagecreatetruecolor( $w, $h );
    473 
    474 	if ( is_gd_image( $dst ) ) {
    475 		$sx = $vert ? ( $w - 1 ) : 0;
    476 		$sy = $horz ? ( $h - 1 ) : 0;
    477 		$sw = $vert ? -$w : $w;
    478 		$sh = $horz ? -$h : $h;
    479 
    480 		if ( imagecopyresampled( $dst, $img, 0, 0, $sx, $sy, $w, $h, $sw, $sh ) ) {
    481 			imagedestroy( $img );
    482 			$img = $dst;
    483 		}
    484 	}
    485 
    486 	return $img;
    487 }
    488 
    489 /**
    490  * Crops an image resource. Internal use only.
    491  *
    492  * @since 2.9.0
    493  *
    494  * @ignore
    495  * @param resource|GdImage $img Image resource or GdImage instance.
    496  * @param float            $x   Source point x-coordinate.
    497  * @param float            $y   Source point y-coordinate.
    498  * @param float            $w   Source width.
    499  * @param float            $h   Source height.
    500  * @return resource|GdImage (maybe) cropped image resource or GdImage instance.
    501  */
    502 function _crop_image_resource( $img, $x, $y, $w, $h ) {
    503 	$dst = wp_imagecreatetruecolor( $w, $h );
    504 
    505 	if ( is_gd_image( $dst ) ) {
    506 		if ( imagecopy( $dst, $img, 0, 0, $x, $y, $w, $h ) ) {
    507 			imagedestroy( $img );
    508 			$img = $dst;
    509 		}
    510 	}
    511 
    512 	return $img;
    513 }
    514 
    515 /**
    516  * Performs group of changes on Editor specified.
    517  *
    518  * @since 2.9.0
    519  *
    520  * @param WP_Image_Editor $image   WP_Image_Editor instance.
    521  * @param array           $changes Array of change operations.
    522  * @return WP_Image_Editor WP_Image_Editor instance with changes applied.
    523  */
    524 function image_edit_apply_changes( $image, $changes ) {
    525 	if ( is_gd_image( $image ) ) {
    526 		/* translators: 1: $image, 2: WP_Image_Editor */
    527 		_deprecated_argument( __FUNCTION__, '3.5.0', sprintf( __( '%1$s needs to be a %2$s object.' ), '$image', 'WP_Image_Editor' ) );
    528 	}
    529 
    530 	if ( ! is_array( $changes ) ) {
    531 		return $image;
    532 	}
    533 
    534 	// Expand change operations.
    535 	foreach ( $changes as $key => $obj ) {
    536 		if ( isset( $obj->r ) ) {
    537 			$obj->type  = 'rotate';
    538 			$obj->angle = $obj->r;
    539 			unset( $obj->r );
    540 		} elseif ( isset( $obj->f ) ) {
    541 			$obj->type = 'flip';
    542 			$obj->axis = $obj->f;
    543 			unset( $obj->f );
    544 		} elseif ( isset( $obj->c ) ) {
    545 			$obj->type = 'crop';
    546 			$obj->sel  = $obj->c;
    547 			unset( $obj->c );
    548 		}
    549 		$changes[ $key ] = $obj;
    550 	}
    551 
    552 	// Combine operations.
    553 	if ( count( $changes ) > 1 ) {
    554 		$filtered = array( $changes[0] );
    555 		for ( $i = 0, $j = 1, $c = count( $changes ); $j < $c; $j++ ) {
    556 			$combined = false;
    557 			if ( $filtered[ $i ]->type == $changes[ $j ]->type ) {
    558 				switch ( $filtered[ $i ]->type ) {
    559 					case 'rotate':
    560 						$filtered[ $i ]->angle += $changes[ $j ]->angle;
    561 						$combined               = true;
    562 						break;
    563 					case 'flip':
    564 						$filtered[ $i ]->axis ^= $changes[ $j ]->axis;
    565 						$combined              = true;
    566 						break;
    567 				}
    568 			}
    569 			if ( ! $combined ) {
    570 				$filtered[ ++$i ] = $changes[ $j ];
    571 			}
    572 		}
    573 		$changes = $filtered;
    574 		unset( $filtered );
    575 	}
    576 
    577 	// Image resource before applying the changes.
    578 	if ( $image instanceof WP_Image_Editor ) {
    579 
    580 		/**
    581 		 * Filters the WP_Image_Editor instance before applying changes to the image.
    582 		 *
    583 		 * @since 3.5.0
    584 		 *
    585 		 * @param WP_Image_Editor $image   WP_Image_Editor instance.
    586 		 * @param array           $changes Array of change operations.
    587 		 */
    588 		$image = apply_filters( 'wp_image_editor_before_change', $image, $changes );
    589 	} elseif ( is_gd_image( $image ) ) {
    590 
    591 		/**
    592 		 * Filters the GD image resource before applying changes to the image.
    593 		 *
    594 		 * @since 2.9.0
    595 		 * @deprecated 3.5.0 Use {@see 'wp_image_editor_before_change'} instead.
    596 		 *
    597 		 * @param resource|GdImage $image   GD image resource or GdImage instance.
    598 		 * @param array            $changes Array of change operations.
    599 		 */
    600 		$image = apply_filters_deprecated( 'image_edit_before_change', array( $image, $changes ), '3.5.0', 'wp_image_editor_before_change' );
    601 	}
    602 
    603 	foreach ( $changes as $operation ) {
    604 		switch ( $operation->type ) {
    605 			case 'rotate':
    606 				if ( 0 != $operation->angle ) {
    607 					if ( $image instanceof WP_Image_Editor ) {
    608 						$image->rotate( $operation->angle );
    609 					} else {
    610 						$image = _rotate_image_resource( $image, $operation->angle );
    611 					}
    612 				}
    613 				break;
    614 			case 'flip':
    615 				if ( 0 != $operation->axis ) {
    616 					if ( $image instanceof WP_Image_Editor ) {
    617 						$image->flip( ( $operation->axis & 1 ) != 0, ( $operation->axis & 2 ) != 0 );
    618 					} else {
    619 						$image = _flip_image_resource( $image, ( $operation->axis & 1 ) != 0, ( $operation->axis & 2 ) != 0 );
    620 					}
    621 				}
    622 				break;
    623 			case 'crop':
    624 				$sel = $operation->sel;
    625 
    626 				if ( $image instanceof WP_Image_Editor ) {
    627 					$size = $image->get_size();
    628 					$w    = $size['width'];
    629 					$h    = $size['height'];
    630 
    631 					$scale = 1 / _image_get_preview_ratio( $w, $h ); // Discard preview scaling.
    632 					$image->crop( $sel->x * $scale, $sel->y * $scale, $sel->w * $scale, $sel->h * $scale );
    633 				} else {
    634 					$scale = 1 / _image_get_preview_ratio( imagesx( $image ), imagesy( $image ) ); // Discard preview scaling.
    635 					$image = _crop_image_resource( $image, $sel->x * $scale, $sel->y * $scale, $sel->w * $scale, $sel->h * $scale );
    636 				}
    637 				break;
    638 		}
    639 	}
    640 
    641 	return $image;
    642 }
    643 
    644 
    645 /**
    646  * Streams image in post to browser, along with enqueued changes
    647  * in `$_REQUEST['history']`.
    648  *
    649  * @since 2.9.0
    650  *
    651  * @param int $post_id Attachment post ID.
    652  * @return bool True on success, false on failure.
    653  */
    654 function stream_preview_image( $post_id ) {
    655 	$post = get_post( $post_id );
    656 
    657 	wp_raise_memory_limit( 'admin' );
    658 
    659 	$img = wp_get_image_editor( _load_image_to_edit_path( $post_id ) );
    660 
    661 	if ( is_wp_error( $img ) ) {
    662 		return false;
    663 	}
    664 
    665 	$changes = ! empty( $_REQUEST['history'] ) ? json_decode( wp_unslash( $_REQUEST['history'] ) ) : null;
    666 	if ( $changes ) {
    667 		$img = image_edit_apply_changes( $img, $changes );
    668 	}
    669 
    670 	// Scale the image.
    671 	$size = $img->get_size();
    672 	$w    = $size['width'];
    673 	$h    = $size['height'];
    674 
    675 	$ratio = _image_get_preview_ratio( $w, $h );
    676 	$w2    = max( 1, $w * $ratio );
    677 	$h2    = max( 1, $h * $ratio );
    678 
    679 	if ( is_wp_error( $img->resize( $w2, $h2 ) ) ) {
    680 		return false;
    681 	}
    682 
    683 	return wp_stream_image( $img, $post->post_mime_type, $post_id );
    684 }
    685 
    686 /**
    687  * Restores the metadata for a given attachment.
    688  *
    689  * @since 2.9.0
    690  *
    691  * @param int $post_id Attachment post ID.
    692  * @return stdClass Image restoration message object.
    693  */
    694 function wp_restore_image( $post_id ) {
    695 	$meta             = wp_get_attachment_metadata( $post_id );
    696 	$file             = get_attached_file( $post_id );
    697 	$backup_sizes     = get_post_meta( $post_id, '_wp_attachment_backup_sizes', true );
    698 	$old_backup_sizes = $backup_sizes;
    699 	$restored         = false;
    700 	$msg              = new stdClass;
    701 
    702 	if ( ! is_array( $backup_sizes ) ) {
    703 		$msg->error = __( 'Cannot load image metadata.' );
    704 		return $msg;
    705 	}
    706 
    707 	$parts         = pathinfo( $file );
    708 	$suffix        = time() . rand( 100, 999 );
    709 	$default_sizes = get_intermediate_image_sizes();
    710 
    711 	if ( isset( $backup_sizes['full-orig'] ) && is_array( $backup_sizes['full-orig'] ) ) {
    712 		$data = $backup_sizes['full-orig'];
    713 
    714 		if ( $parts['basename'] != $data['file'] ) {
    715 			if ( defined( 'IMAGE_EDIT_OVERWRITE' ) && IMAGE_EDIT_OVERWRITE ) {
    716 
    717 				// Delete only if it's an edited image.
    718 				if ( preg_match( '/-e[0-9]{13}\./', $parts['basename'] ) ) {
    719 					wp_delete_file( $file );
    720 				}
    721 			} elseif ( isset( $meta['width'], $meta['height'] ) ) {
    722 				$backup_sizes[ "full-$suffix" ] = array(
    723 					'width'  => $meta['width'],
    724 					'height' => $meta['height'],
    725 					'file'   => $parts['basename'],
    726 				);
    727 			}
    728 		}
    729 
    730 		$restored_file = path_join( $parts['dirname'], $data['file'] );
    731 		$restored      = update_attached_file( $post_id, $restored_file );
    732 
    733 		$meta['file']   = _wp_relative_upload_path( $restored_file );
    734 		$meta['width']  = $data['width'];
    735 		$meta['height'] = $data['height'];
    736 	}
    737 
    738 	foreach ( $default_sizes as $default_size ) {
    739 		if ( isset( $backup_sizes[ "$default_size-orig" ] ) ) {
    740 			$data = $backup_sizes[ "$default_size-orig" ];
    741 			if ( isset( $meta['sizes'][ $default_size ] ) && $meta['sizes'][ $default_size ]['file'] != $data['file'] ) {
    742 				if ( defined( 'IMAGE_EDIT_OVERWRITE' ) && IMAGE_EDIT_OVERWRITE ) {
    743 
    744 					// Delete only if it's an edited image.
    745 					if ( preg_match( '/-e[0-9]{13}-/', $meta['sizes'][ $default_size ]['file'] ) ) {
    746 						$delete_file = path_join( $parts['dirname'], $meta['sizes'][ $default_size ]['file'] );
    747 						wp_delete_file( $delete_file );
    748 					}
    749 				} else {
    750 					$backup_sizes[ "$default_size-{$suffix}" ] = $meta['sizes'][ $default_size ];
    751 				}
    752 			}
    753 
    754 			$meta['sizes'][ $default_size ] = $data;
    755 		} else {
    756 			unset( $meta['sizes'][ $default_size ] );
    757 		}
    758 	}
    759 
    760 	if ( ! wp_update_attachment_metadata( $post_id, $meta ) ||
    761 		( $old_backup_sizes !== $backup_sizes && ! update_post_meta( $post_id, '_wp_attachment_backup_sizes', $backup_sizes ) ) ) {
    762 
    763 		$msg->error = __( 'Cannot save image metadata.' );
    764 		return $msg;
    765 	}
    766 
    767 	if ( ! $restored ) {
    768 		$msg->error = __( 'Image metadata is inconsistent.' );
    769 	} else {
    770 		$msg->msg = __( 'Image restored successfully.' );
    771 	}
    772 
    773 	return $msg;
    774 }
    775 
    776 /**
    777  * Saves image to post, along with enqueued changes
    778  * in `$_REQUEST['history']`.
    779  *
    780  * @since 2.9.0
    781  *
    782  * @param int $post_id Attachment post ID.
    783  * @return stdClass
    784  */
    785 function wp_save_image( $post_id ) {
    786 	$_wp_additional_image_sizes = wp_get_additional_image_sizes();
    787 
    788 	$return  = new stdClass;
    789 	$success = false;
    790 	$delete  = false;
    791 	$scaled  = false;
    792 	$nocrop  = false;
    793 	$post    = get_post( $post_id );
    794 
    795 	$img = wp_get_image_editor( _load_image_to_edit_path( $post_id, 'full' ) );
    796 	if ( is_wp_error( $img ) ) {
    797 		$return->error = esc_js( __( 'Unable to create new image.' ) );
    798 		return $return;
    799 	}
    800 
    801 	$fwidth  = ! empty( $_REQUEST['fwidth'] ) ? (int) $_REQUEST['fwidth'] : 0;
    802 	$fheight = ! empty( $_REQUEST['fheight'] ) ? (int) $_REQUEST['fheight'] : 0;
    803 	$target  = ! empty( $_REQUEST['target'] ) ? preg_replace( '/[^a-z0-9_-]+/i', '', $_REQUEST['target'] ) : '';
    804 	$scale   = ! empty( $_REQUEST['do'] ) && 'scale' === $_REQUEST['do'];
    805 
    806 	if ( $scale && $fwidth > 0 && $fheight > 0 ) {
    807 		$size = $img->get_size();
    808 		$sX   = $size['width'];
    809 		$sY   = $size['height'];
    810 
    811 		// Check if it has roughly the same w / h ratio.
    812 		$diff = round( $sX / $sY, 2 ) - round( $fwidth / $fheight, 2 );
    813 		if ( -0.1 < $diff && $diff < 0.1 ) {
    814 			// Scale the full size image.
    815 			if ( $img->resize( $fwidth, $fheight ) ) {
    816 				$scaled = true;
    817 			}
    818 		}
    819 
    820 		if ( ! $scaled ) {
    821 			$return->error = esc_js( __( 'Error while saving the scaled image. Please reload the page and try again.' ) );
    822 			return $return;
    823 		}
    824 	} elseif ( ! empty( $_REQUEST['history'] ) ) {
    825 		$changes = json_decode( wp_unslash( $_REQUEST['history'] ) );
    826 		if ( $changes ) {
    827 			$img = image_edit_apply_changes( $img, $changes );
    828 		}
    829 	} else {
    830 		$return->error = esc_js( __( 'Nothing to save, the image has not changed.' ) );
    831 		return $return;
    832 	}
    833 
    834 	$meta         = wp_get_attachment_metadata( $post_id );
    835 	$backup_sizes = get_post_meta( $post->ID, '_wp_attachment_backup_sizes', true );
    836 
    837 	if ( ! is_array( $meta ) ) {
    838 		$return->error = esc_js( __( 'Image data does not exist. Please re-upload the image.' ) );
    839 		return $return;
    840 	}
    841 
    842 	if ( ! is_array( $backup_sizes ) ) {
    843 		$backup_sizes = array();
    844 	}
    845 
    846 	// Generate new filename.
    847 	$path = get_attached_file( $post_id );
    848 
    849 	$basename = pathinfo( $path, PATHINFO_BASENAME );
    850 	$dirname  = pathinfo( $path, PATHINFO_DIRNAME );
    851 	$ext      = pathinfo( $path, PATHINFO_EXTENSION );
    852 	$filename = pathinfo( $path, PATHINFO_FILENAME );
    853 	$suffix   = time() . rand( 100, 999 );
    854 
    855 	if ( defined( 'IMAGE_EDIT_OVERWRITE' ) && IMAGE_EDIT_OVERWRITE &&
    856 		isset( $backup_sizes['full-orig'] ) && $backup_sizes['full-orig']['file'] != $basename ) {
    857 
    858 		if ( 'thumbnail' === $target ) {
    859 			$new_path = "{$dirname}/{$filename}-temp.{$ext}";
    860 		} else {
    861 			$new_path = $path;
    862 		}
    863 	} else {
    864 		while ( true ) {
    865 			$filename     = preg_replace( '/-e([0-9]+)$/', '', $filename );
    866 			$filename    .= "-e{$suffix}";
    867 			$new_filename = "{$filename}.{$ext}";
    868 			$new_path     = "{$dirname}/$new_filename";
    869 			if ( file_exists( $new_path ) ) {
    870 				$suffix++;
    871 			} else {
    872 				break;
    873 			}
    874 		}
    875 	}
    876 
    877 	// Save the full-size file, also needed to create sub-sizes.
    878 	if ( ! wp_save_image_file( $new_path, $img, $post->post_mime_type, $post_id ) ) {
    879 		$return->error = esc_js( __( 'Unable to save the image.' ) );
    880 		return $return;
    881 	}
    882 
    883 	if ( 'nothumb' === $target || 'all' === $target || 'full' === $target || $scaled ) {
    884 		$tag = false;
    885 		if ( isset( $backup_sizes['full-orig'] ) ) {
    886 			if ( ( ! defined( 'IMAGE_EDIT_OVERWRITE' ) || ! IMAGE_EDIT_OVERWRITE ) && $backup_sizes['full-orig']['file'] !== $basename ) {
    887 				$tag = "full-$suffix";
    888 			}
    889 		} else {
    890 			$tag = 'full-orig';
    891 		}
    892 
    893 		if ( $tag ) {
    894 			$backup_sizes[ $tag ] = array(
    895 				'width'  => $meta['width'],
    896 				'height' => $meta['height'],
    897 				'file'   => $basename,
    898 			);
    899 		}
    900 		$success = ( $path === $new_path ) || update_attached_file( $post_id, $new_path );
    901 
    902 		$meta['file'] = _wp_relative_upload_path( $new_path );
    903 
    904 		$size           = $img->get_size();
    905 		$meta['width']  = $size['width'];
    906 		$meta['height'] = $size['height'];
    907 
    908 		if ( $success && ( 'nothumb' === $target || 'all' === $target ) ) {
    909 			$sizes = get_intermediate_image_sizes();
    910 			if ( 'nothumb' === $target ) {
    911 				$sizes = array_diff( $sizes, array( 'thumbnail' ) );
    912 			}
    913 		}
    914 
    915 		$return->fw = $meta['width'];
    916 		$return->fh = $meta['height'];
    917 	} elseif ( 'thumbnail' === $target ) {
    918 		$sizes   = array( 'thumbnail' );
    919 		$success = true;
    920 		$delete  = true;
    921 		$nocrop  = true;
    922 	}
    923 
    924 	/*
    925 	 * We need to remove any existing resized image files because
    926 	 * a new crop or rotate could generate different sizes (and hence, filenames),
    927 	 * keeping the new resized images from overwriting the existing image files.
    928 	 * https://core.trac.wordpress.org/ticket/32171
    929 	 */
    930 	if ( defined( 'IMAGE_EDIT_OVERWRITE' ) && IMAGE_EDIT_OVERWRITE && ! empty( $meta['sizes'] ) ) {
    931 		foreach ( $meta['sizes'] as $size ) {
    932 			if ( ! empty( $size['file'] ) && preg_match( '/-e[0-9]{13}-/', $size['file'] ) ) {
    933 				$delete_file = path_join( $dirname, $size['file'] );
    934 				wp_delete_file( $delete_file );
    935 			}
    936 		}
    937 	}
    938 
    939 	if ( isset( $sizes ) ) {
    940 		$_sizes = array();
    941 
    942 		foreach ( $sizes as $size ) {
    943 			$tag = false;
    944 			if ( isset( $meta['sizes'][ $size ] ) ) {
    945 				if ( isset( $backup_sizes[ "$size-orig" ] ) ) {
    946 					if ( ( ! defined( 'IMAGE_EDIT_OVERWRITE' ) || ! IMAGE_EDIT_OVERWRITE ) && $backup_sizes[ "$size-orig" ]['file'] != $meta['sizes'][ $size ]['file'] ) {
    947 						$tag = "$size-$suffix";
    948 					}
    949 				} else {
    950 					$tag = "$size-orig";
    951 				}
    952 
    953 				if ( $tag ) {
    954 					$backup_sizes[ $tag ] = $meta['sizes'][ $size ];
    955 				}
    956 			}
    957 
    958 			if ( isset( $_wp_additional_image_sizes[ $size ] ) ) {
    959 				$width  = (int) $_wp_additional_image_sizes[ $size ]['width'];
    960 				$height = (int) $_wp_additional_image_sizes[ $size ]['height'];
    961 				$crop   = ( $nocrop ) ? false : $_wp_additional_image_sizes[ $size ]['crop'];
    962 			} else {
    963 				$height = get_option( "{$size}_size_h" );
    964 				$width  = get_option( "{$size}_size_w" );
    965 				$crop   = ( $nocrop ) ? false : get_option( "{$size}_crop" );
    966 			}
    967 
    968 			$_sizes[ $size ] = array(
    969 				'width'  => $width,
    970 				'height' => $height,
    971 				'crop'   => $crop,
    972 			);
    973 		}
    974 
    975 		$meta['sizes'] = array_merge( $meta['sizes'], $img->multi_resize( $_sizes ) );
    976 	}
    977 
    978 	unset( $img );
    979 
    980 	if ( $success ) {
    981 		wp_update_attachment_metadata( $post_id, $meta );
    982 		update_post_meta( $post_id, '_wp_attachment_backup_sizes', $backup_sizes );
    983 
    984 		if ( 'thumbnail' === $target || 'all' === $target || 'full' === $target ) {
    985 			// Check if it's an image edit from attachment edit screen.
    986 			if ( ! empty( $_REQUEST['context'] ) && 'edit-attachment' === $_REQUEST['context'] ) {
    987 				$thumb_url         = wp_get_attachment_image_src( $post_id, array( 900, 600 ), true );
    988 				$return->thumbnail = $thumb_url[0];
    989 			} else {
    990 				$file_url = wp_get_attachment_url( $post_id );
    991 				if ( ! empty( $meta['sizes']['thumbnail'] ) ) {
    992 					$thumb             = $meta['sizes']['thumbnail'];
    993 					$return->thumbnail = path_join( dirname( $file_url ), $thumb['file'] );
    994 				} else {
    995 					$return->thumbnail = "$file_url?w=128&h=128";
    996 				}
    997 			}
    998 		}
    999 	} else {
   1000 		$delete = true;
   1001 	}
   1002 
   1003 	if ( $delete ) {
   1004 		wp_delete_file( $new_path );
   1005 	}
   1006 
   1007 	$return->msg = esc_js( __( 'Image saved' ) );
   1008 	return $return;
   1009 }