PluginProbe ʕ •ᴥ•ʔ
Admin and Site Enhancements (ASE) / 7.6.2
Admin and Site Enhancements (ASE) v7.6.2
9.1.0 9.0.2 9.0.1 9.0.0 8.9.2 8.9.1 8.9.0 8.8.8 8.8.7 8.8.6 8.8.5 8.8.4 8.8.3 8.8.2 8.8.1 8.8.0 8.7.3 8.7.2 8.7.1 8.2.1 8.2.2 8.2.3 8.3.0 8.3.1 8.3.2 8.4.0 8.4.1 8.4.2 8.5.0 8.5.1 8.5.2 8.6.0 8.6.1 8.6.2 8.7.0 5.0.1 5.0.2 5.0.2.1 5.0.2.2 5.0.2.3 5.0.2.4 5.1.0 5.2.0 5.2.1 5.2.10 5.2.11 5.2.2 5.2.3 5.2.4 5.2.5 5.2.6 5.2.7 5.2.8 5.2.9 5.3.0 5.3.1 5.3.2 5.4.0 5.4.1 5.5.0 5.5.1 5.5.2 5.6.0 5.6.1 5.6.2 5.7.0 5.7.1 5.8.0 5.8.1 6.0.0 6.0.3 6.0.4 6.0.5 6.0.5.1 6.0.6 6.0.7 6.0.8.1 6.1.0 6.1.3 6.2.0 6.2.1 6.2.2 6.2.3 6.2.4 6.2.5 6.2.6 6.2.7 6.3.0 6.3.1 6.3.2 6.4.0 6.5.0 6.5.1 6.6.0 6.7.0 6.8.0 6.8.2 6.8.3 6.9.0 6.9.1 6.9.10 6.9.11 6.9.12 6.9.13.1 6.9.13.2 6.9.2 6.9.3 6.9.4 6.9.5 6.9.6 6.9.7 6.9.8 6.9.9 7.0.0 7.0.2 7.0.2.1 7.0.2.2 7.0.2.3 7.0.3 7.1.0 7.1.1 7.1.2 7.1.3 7.1.4 7.1.5 7.2.0 7.2.1 7.3.0 7.3.1 7.3.2 7.3.3 7.4.0 7.4.2 7.4.4 7.4.5 7.4.6 7.4.7 7.4.8 7.5.0 7.5.1 7.5.2 7.5.3 7.5.4 7.6.0 7.6.1 7.6.1.1 7.6.10 7.6.11 7.6.2 7.6.3 7.6.4 7.6.5 7.6.6 7.6.7 7.6.7.1 7.6.8 7.6.9 7.7.0 7.8.0 7.8.1 7.8.10 7.8.11 7.8.12 7.8.13 7.8.14 7.8.15 7.8.16 7.8.17 7.8.18 7.8.2 7.8.3 7.8.4 7.8.5 7.8.5.1 7.8.6 7.8.7 7.8.8 7.8.9 7.9.0 7.9.1 7.9.10 7.9.11 7.9.2 7.9.3 7.9.4 7.9.5 7.9.6 7.9.7 7.9.8 7.9.9 8.0.0 8.0.1 8.0.2 8.0.3 8.0.4 8.0.5 8.0.6 8.0.7 8.0.8 8.1.0 8.1.1 8.1.2 8.1.3 8.1.4 8.2.0 trunk 1.0.0 1.1.0 1.2.0 1.3.0 1.4.0 1.5.0 1.6.0 1.7.0 1.8.0 1.9.0 2.0.0 2.1.0 2.2.0 2.3.0 2.4.0 2.5.0 2.6.0 2.7.0 2.8.0 2.8.1 2.8.2 2.8.3 2.9.0 3.0.0 3.0.1 3.0.2 3.0.3 3.0.4 3.0.5 3.0.6 3.1.0 3.2.0 3.3.0 3.4.0 3.5.0 3.6.1 3.7.0 3.8.0 3.9.0 3.9.1 3.9.2 4.0.0 4.0.1 4.1.0 4.2.0 4.2.1 4.2.2 4.3.0 4.3.1 4.4.0 4.5.0 4.6.0 4.7.0 4.7.1 4.7.2 4.7.3 4.7.4 4.8.0 4.8.1 4.8.2 4.8.3 4.9.0 4.9.1 4.9.2 4.9.3 5.0.0
admin-site-enhancements / classes / class-image-upload-control.php
admin-site-enhancements / classes Last commit date
class-activation.php 1 year ago class-admin-menu-organizer.php 1 year ago class-auto-publish-posts-with-missed-schedule.php 1 year ago class-avif-upload.php 1 year ago class-change-login-url.php 1 year ago class-cleanup-admin-bar.php 1 year ago class-common-methods.php 1 year ago class-content-duplication.php 1 year ago class-content-order.php 1 year ago class-custom-admin-footer-text.php 1 year ago class-custom-body-class.php 1 year ago class-custom-css.php 1 year ago class-custom-nav-menu-items-in-new-tab.php 1 year ago class-deactivation.php 1 year ago class-disable-comments.php 1 year ago class-disable-dashboard-widgets.php 1 year ago class-disable-feeds.php 1 year ago class-disable-gutenberg.php 1 year ago class-disable-rest-api.php 1 year ago class-disable-smaller-components.php 1 year ago class-disable-updates.php 1 year ago class-disable-xml-rpc.php 1 year ago class-display-system-summary.php 1 year ago class-email-address-obfuscator.php 1 year ago class-email-delivery.php 1 year ago class-enhance-list-tables.php 1 year ago class-external-permalinks.php 1 year ago class-heartbeat-control.php 1 year ago class-hide-admin-bar.php 1 year ago class-hide-admin-notices.php 1 year ago class-image-sizes-panel.php 1 year ago class-image-upload-control.php 1 year ago class-insert-head-body-footer-code.php 1 year ago class-last-login-column.php 1 year ago class-limit-login-attempts.php 1 year ago class-login-id-type.php 1 year ago class-login-logout-menu.php 1 year ago class-maintenance-mode.php 1 year ago class-manage-ads-appads-txt.php 1 year ago class-manage-robots-txt.php 1 year ago class-media-replacement.php 1 year ago class-multiple-user-roles.php 1 year ago class-obfuscate-author-slugs.php 1 year ago class-open-external-links-in-new-tab.php 1 year ago class-password-protection.php 1 year ago class-redirect-after-login.php 1 year ago class-redirect-after-logout.php 1 year ago class-redirect-fourofour.php 1 year ago class-registration-date-column.php 1 year ago class-revisions-control.php 1 year ago class-search-engines-visibility.php 1 year ago class-settings-fields-render.php 1 year ago class-settings-sanitization.php 1 year ago class-settings-sections-fields.php 1 year ago class-show-custom-taxonomy-filters.php 1 year ago class-site-identity-on-login-page.php 1 year ago class-svg-upload.php 1 year ago class-various-admin-ui-enhancements.php 1 year ago class-view-admin-as-role.php 1 year ago class-wider-admin-menu.php 1 year ago class-wp-config-transformer.php 1 year ago
class-image-upload-control.php
472 lines
1 <?php
2
3 namespace ASENHA\Classes;
4
5 use Imagick;
6 /**
7 * Class for Image Upload Control module
8 *
9 * @since 6.9.5
10 */
11 class Image_Upload_Control {
12 public $png_is_transparent;
13
14 /**
15 * Array storing the file names that were processed, as keys.
16 *
17 * @since 7.5.0
18 * @link https://plugins.trac.wordpress.org/browser/fix-image-rotation/tags/2.2.2/includes/class-fix-image-rotation.php#L30
19 *
20 * @access private
21 *
22 * @var array
23 */
24 private $orientation_fixed;
25
26 /**
27 * Array storing the meta data of original files in case it
28 * needs to be restored later.
29 *
30 * @since 7.5.0
31 * @link https://plugins.trac.wordpress.org/browser/fix-image-rotation/tags/2.2.2/includes/class-fix-image-rotation.php#L42
32 *
33 * @access private
34 *
35 * @var array
36 */
37 private $previous_meta;
38
39 /**
40 * Constructor
41 * @since 7.4.3
42 */
43 function __construct() {
44 $this->png_is_transparent = false;
45 $this->orientation_fixed = array();
46 $this->previous_meta = array();
47 }
48
49 /**
50 * Handler for image uploads. Convert and resize images.
51 *
52 * @since 4.3.0
53 */
54 public function image_upload_handler( $upload ) {
55 $applicable_mime_types = array(
56 'image/bmp',
57 'image/x-ms-bmp',
58 'image/png',
59 'image/jpeg',
60 'image/jpg',
61 'image/webp'
62 );
63 if ( in_array( $upload['type'], $applicable_mime_types ) ) {
64 // Exlude from conversion and resizing images with filenames ending with '-nr', e.g. birds-nr.png
65 if ( false !== strpos( $upload['file'], '-nr.' ) ) {
66 return $upload;
67 }
68 // Convert BMP
69 if ( 'image/bmp' === $upload['type'] || 'image/x-ms-bmp' === $upload['type'] ) {
70 $upload = $this->maybe_convert_image( 'bmp', $upload );
71 }
72 // Convert PNG without transparency
73 if ( 'image/png' === $upload['type'] ) {
74 $upload = $this->maybe_convert_image( 'png', $upload );
75 }
76 // At this point, BMPs and non-transparent PNGs are already converted to JPGs, unless excluded with '-nr' suffix.
77 // Let's perform resize operation as needed, i.e. if image dimension is larger than specified
78 $mime_types_to_resize = array(
79 'image/jpeg',
80 'image/jpg',
81 'image/png',
82 'image/webp'
83 );
84 if ( !is_wp_error( $upload ) && in_array( $upload['type'], $mime_types_to_resize ) && filesize( $upload['file'] ) > 0 ) {
85 // https://developer.wordpress.org/reference/classes/wp_image_editor/
86 $wp_image_editor = wp_get_image_editor( $upload['file'] );
87 if ( !is_wp_error( $wp_image_editor ) ) {
88 $image_size = $wp_image_editor->get_size();
89 $options = get_option( ASENHA_SLUG_U, array() );
90 $max_width = $options['image_max_width'];
91 $max_height = $options['image_max_height'];
92 // Check upload image's dimension and only resize if larger than the defined max dimension
93 if ( isset( $image_size['width'] ) && $image_size['width'] > $max_width || isset( $image_size['height'] ) && $image_size['height'] > $max_height ) {
94 $wp_image_editor->resize( $max_width, $max_height, false );
95 // false is for no cropping
96 if ( 'image/jpg' === $upload['type'] || 'image/jpeg' === $upload['type'] ) {
97 $wp_image_editor->set_quality( 90 );
98 // default is 82
99 }
100 $wp_image_editor->save( $upload['file'] );
101 }
102 }
103 }
104 }
105 return $upload;
106 }
107
108 /**
109 * Convert BMP or PNG without transparency into JPG
110 *
111 * @since 4.3.0
112 */
113 public function maybe_convert_image( $file_extension, $upload ) {
114 $image_object = null;
115 // Get image object from uploaded BMP/PNG
116 if ( 'bmp' === $file_extension ) {
117 if ( is_file( $upload['file'] ) ) {
118 // Generate image object from BMP for conversion to JPG later
119 if ( function_exists( 'imagecreatefrombmp' ) ) {
120 // PHP >= v7.2
121 $image_object = imagecreatefrombmp( $upload['file'] );
122 } else {
123 // PHP < v7.2
124 require_once ASENHA_PATH . 'includes/bmp-to-image-object.php';
125 $image_object = bmp_to_image_object( $upload['file'] );
126 }
127 }
128 }
129 if ( 'png' === $file_extension ) {
130 // Detect alpha/transparency in PNG
131 $this->png_is_transparent = false;
132 if ( is_file( $upload['file'] ) ) {
133 if ( function_exists( 'imagecreatefrompng' ) ) {
134 // GD library is present, so 'imagecreatefrompng' function is available
135 // Generate image object from PNG for potential conversion to JPG later.
136 $image_object = imagecreatefrompng( $upload['file'] );
137 // Get image dimension
138 list( $width, $height ) = getimagesize( $upload['file'] );
139 // Run through pixels until transparent pixel is found
140 for ($x = 0; $x < $width; $x++) {
141 for ($y = 0; $y < $height; $y++) {
142 $pixel_color_index = imagecolorat( $image_object, $x, $y );
143 $pixel_rgba = imagecolorsforindex( $image_object, $pixel_color_index );
144 // array of red, green, blue and alpha values
145 if ( $pixel_rgba['alpha'] > 0 ) {
146 // a pixel with alpha/transparency has been found
147 // alpha value range from 0 (completely opaque) to 127 (fully transparent).
148 // Ref: https://www.php.net/manual/en/function.imagecolorallocatealpha.php
149 $this->png_is_transparent = true;
150 break 2;
151 // Break both 'for' loops
152 }
153 }
154 }
155 } else {
156 if ( class_exists( 'Imagick' ) ) {
157 $imagick = new Imagick();
158 $imagick->readImage( $upload['file'] );
159 // Ref: https://stackoverflow.com/a/52295997
160 // Ref: https://www.php.net/manual/en/imagick.getimagechannelrange.php
161 // If the channel is defined, and has any transparent areas across any frame, then maxima will always be greater then minima.
162 // If the channel is NOT defined, then minima will be Inf placeholder, and maxima will be -Inf placeholder, so the above check will still work.
163 $alpha_range = $imagick->getImageChannelRange( Imagick::CHANNEL_ALPHA );
164 $this->png_is_transparent = $alpha_range['minima'] < $alpha_range['maxima'];
165 }
166 }
167 }
168 // Do not convert PNG with alpha/transparency
169 if ( $this->png_is_transparent ) {
170 return $upload;
171 }
172 }
173 // Let's convert BMP and non-transparent PNG into JPG
174 if ( is_object( $image_object ) || class_exists( 'Imagick' ) ) {
175 $wp_uploads = wp_upload_dir();
176 $old_filename = wp_basename( $upload['file'] );
177 // Assign new, unique file name for the converted image
178 // $new_filename = wp_basename( str_ireplace( '.' . $file_extension, '.jpg', $old_filename ) );
179 $new_filename = str_ireplace( '.' . $file_extension, '.jpg', $old_filename );
180 $new_filename = wp_unique_filename( dirname( $upload['file'] ), $new_filename );
181 // original image is always deleted in ASE Free
182 $keep_original_image = false;
183 $converted_to_jpg = false;
184 }
185 if ( is_object( $image_object ) ) {
186 // When image object creation is successful
187 // When conversion from BMP/PNG to JPG is successful using GD. Last parameter is JPG quality (0-100).
188 if ( imagejpeg( $image_object, $wp_uploads['path'] . '/' . $new_filename, 90 ) ) {
189 $converted_to_jpg = true;
190 }
191 } else {
192 // When image object creation with imagecreatefrombmp(), bmp_to_image_object() or imagecreatefrompng() is not successful, we use Imagick to convert from BMP and non-transparent PNG to JPG.
193 if ( class_exists( 'Imagick' ) ) {
194 $imagick = new Imagick();
195 $imagick->readImage( $upload['file'] );
196 $imagick->setImageCompressionQuality( 90 );
197 $imagick->setImageFormat( 'jpg' );
198 // $imagick->setFormat( 'jpg' );
199 if ( $imagick->writeImage( $wp_uploads['path'] . '/' . $new_filename ) ) {
200 $converted_to_jpg = true;
201 }
202 // Clear the Imagick object
203 $imagick->clear();
204 $imagick->destroy();
205 }
206 }
207 if ( $converted_to_jpg ) {
208 // Delete original BMP / PNG
209 if ( !$keep_original_image ) {
210 unlink( $upload['file'] );
211 }
212 // Add converted JPG info into $upload
213 $upload['file'] = $wp_uploads['path'] . '/' . $new_filename;
214 $upload['url'] = $wp_uploads['url'] . '/' . $new_filename;
215 $upload['type'] = 'image/jpeg';
216 }
217 return $upload;
218 }
219
220 /**
221 * Generate image object from PNG/JPG with GD library
222 *
223 * @since 6.9.11
224 */
225 public function gd_generate_webp(
226 $file,
227 $file_extension,
228 $webp_path,
229 $webp_conversion_quality
230 ) {
231 if ( 'png' == $file_extension ) {
232 $image_object = imagecreatefrompng( $file );
233 if ( $this->png_is_transparent ) {
234 imagepalettetotruecolor( $image_object );
235 }
236 }
237 if ( 'jpg' == $file_extension || 'jpeg' == $file_extension ) {
238 $image_object = imagecreatefromjpeg( $file );
239 }
240 // When creation of image object from PNG/JPG is successful. let's generate WebP image
241 // Second parameter is file path, last parameter is WebP quality (0-100).
242 if ( !is_null( $image_object ) && is_object( $image_object ) ) {
243 imagewebp( $image_object, $webp_path, $webp_conversion_quality );
244 }
245 }
246
247 /**
248 * Checks the filename before it is uploaded to WordPress and
249 * runs the fix_image_orientation function in case its needed.
250 *
251 * @since 7.5.0
252 * @link https://plugins.trac.wordpress.org/browser/fix-image-rotation/tags/2.2.2/includes/class-fix-image-rotation.php#L172
253 *
254 * @access public
255 *
256 * @hook wp_handle_upload_prefilter
257 *
258 * @param array $file An array of data for a single file.
259 *
260 * @return array An array of data for a single file.
261 */
262 public function prefilter_maybe_fix_image_orientation( $file ) {
263 // Get the file extension
264 // $suffix = substr( $file['name'], strrpos( $file['name'], '.', -1 ) + 1 );
265 $suffix = pathinfo( $file['name'], PATHINFO_EXTENSION );
266 if ( in_array( strtolower( $suffix ), array('jpg', 'jpeg', 'tiff'), true ) ) {
267 $this->fix_image_orientation( $file['tmp_name'] );
268 }
269 return $file;
270 }
271
272 /**
273 * Checks the filename before it is uploaded to WordPress and
274 * runs the fix_image_orientation function in case its needed.
275 *
276 * @since 7.5.0
277 * @link https://plugins.trac.wordpress.org/browser/fix-image-rotation/tags/2.2.2/includes/class-fix-image-rotation.php#L150
278 *
279 * @access public
280 *
281 * @hook wp_handle_upload
282 *
283 * @param array $file {
284 * Array of upload data.
285 *
286 * @type string $file Filename of the newly-uploaded file.
287 * @type string $url URL of the uploaded file.
288 * @type string $type File type.
289 * }
290 *
291 * @return array Array of upload data.
292 */
293 public function maybe_fix_image_orientation( $file ) {
294 $suffix = substr( $file['file'], strrpos( $file['file'], '.', -1 ) + 1 );
295 if ( in_array( strtolower( $suffix ), array('jpg', 'jpeg', 'tiff'), true ) ) {
296 $this->fix_image_orientation( $file['file'] );
297 }
298 return $file;
299 }
300
301 /**
302 * Fixes the orientation of the image based on exif data
303 *
304 * @since 7.5.0
305 * @link https://plugins.trac.wordpress.org/browser/fix-image-rotation/tags/2.2.2/includes/class-fix-image-rotation.php#L191
306 *
307 * @access public
308 *
309 * @param string $file Path of the file.
310 *
311 * @return void
312 */
313 public function fix_image_orientation( $file ) {
314 if ( !isset( $this->orientation_fixed[$file] ) ) {
315 $exif = exif_read_data( $file );
316 if ( isset( $exif ) && isset( $exif['Orientation'] ) && $exif['Orientation'] > 1 ) {
317 // Need it so that image editors are available to us.
318 // include_once ABSPATH . 'wp-admin/includes/image-edit.php';
319 // Calculate the operations we need to perform on the image.
320 $operations = $this->calculate_flip_and_rotate( $file, $exif );
321 if ( false !== $operations ) {
322 // Lets flip flop and rotate the image as needed.
323 $this->do_flip_and_rotate( $file, $operations );
324 }
325 }
326 }
327 }
328
329 /**
330 * Calculate the flips and rotations image will need to do to fix its orientation.
331 *
332 * @since 7.5.0
333 * @link https://plugins.trac.wordpress.org/browser/fix-image-rotation/tags/2.2.2/includes/class-fix-image-rotation.php#L225
334 *
335 * @access private
336 *
337 * @param string $file Path of the file.
338 *
339 * @param array $exif Exif data of the image.
340 *
341 * @return array|bool Array of operations to be performed on the image,
342 * false if no operations are needed.
343 */
344 private function calculate_flip_and_rotate( $file, $exif ) {
345 $rotator = false;
346 $flipper = false;
347 $orientation = 0;
348 // Lets switch to the orientation defined in the exif data.
349 switch ( $exif['Orientation'] ) {
350 case 1:
351 // We don't want to fix an already correct image :).
352 $this->orientation_fixed[$file] = true;
353 return false;
354 case 2:
355 $flipper = array(false, true);
356 break;
357 case 3:
358 $orientation = -180;
359 $rotator = true;
360 break;
361 case 4:
362 $flipper = array(true, false);
363 break;
364 case 5:
365 $orientation = -90;
366 $rotator = true;
367 $flipper = array(false, true);
368 break;
369 case 6:
370 $orientation = -90;
371 $rotator = true;
372 break;
373 case 7:
374 $orientation = -270;
375 $rotator = true;
376 $flipper = array(false, true);
377 break;
378 case 8:
379 case 9:
380 $orientation = -270;
381 $rotator = true;
382 break;
383 default:
384 $orientation = 0;
385 $rotator = true;
386 break;
387 }
388 return compact( 'orientation', 'rotator', 'flipper' );
389 }
390
391 /**
392 * Flips and rotates the image based on the parameters provided.
393 *
394 * @since 7.5.0
395 * @link https://plugins.trac.wordpress.org/browser/fix-image-rotation/tags/2.2.2/includes/class-fix-image-rotation.php#L299
396 *
397 * @access private
398 *
399 * @param string $file Path of the file.
400 *
401 * @param array $operations {
402 * Array of operations to be performed on the image.
403 *
404 * @type bool $rotator Whether to rotate the image or not.
405 * @type int $orientation Amount of rotation to be performed in degrees.
406 * @type array|bool $flipper {
407 * Whether to flip the image or not, false if no flipping needed.
408 *
409 * @type bool $0 Flip along Horizontal Axis.
410 * @type bool $1 Flip along Vertical Axis.
411 * }
412 * }
413 *
414 * @return bool Returns true if operations were successful, false otherwise.
415 */
416 private function do_flip_and_rotate( $file, $operations ) {
417 $editor = wp_get_image_editor( $file );
418 // If GD Library is being used, then we need to store metadata to restore later.
419 if ( 'WP_Image_Editor_GD' === get_class( $editor ) ) {
420 include_once ABSPATH . 'wp-admin/includes/image.php';
421 $this->previous_meta[$file] = wp_read_image_metadata( $file );
422 }
423 if ( !is_wp_error( $editor ) ) {
424 // Lets rotate and flip the image based on exif orientation.
425 if ( true === $operations['rotator'] ) {
426 $editor->rotate( $operations['orientation'] );
427 }
428 if ( false !== $operations['flipper'] ) {
429 $editor->flip( $operations['flipper'][0], $operations['flipper'][1] );
430 }
431 $editor->save( $file );
432 $this->orientation_fixed[$file] = true;
433 add_filter(
434 'wp_read_image_metadata',
435 array($this, 'restore_meta_data'),
436 10,
437 2
438 );
439 return true;
440 }
441 return false;
442 }
443
444 /**
445 * Restores the meta data of the image after being processed.
446 *
447 * WordPress' Imagick Library does not need this, but GD library
448 * removes metadata from the image upon rotation or flip so this
449 * method restores those values.
450 *
451 * @since 7.5.0
452 * @link https://plugins.trac.wordpress.org/browser/fix-image-rotation/tags/2.2.2/includes/class-fix-image-rotation.php#L341
453 *
454 * @hook wp_read_image_metadata
455 *
456 * @param array $meta Image meta data.
457 * @param string $file Path to image file.
458 *
459 * @return array Image meta data.
460 */
461 public function restore_meta_data( $meta, $file ) {
462 if ( isset( $this->previous_meta[$file] ) ) {
463 $meta = $this->previous_meta[$file];
464 // Setting the Orientation meta to the new value after fixing the rotation.
465 $meta['orientation'] = 1;
466 return $meta;
467 }
468 return $meta;
469 }
470
471 }
472