PluginProbe ʕ •ᴥ•ʔ
Admin and Site Enhancements (ASE) / 7.9.7
Admin and Site Enhancements (ASE) v7.9.7
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-captcha-protection.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-author-archives.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
478 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 $options = get_option( ASENHA_SLUG_U, array() );
56 $applicable_mime_types = array(
57 'image/bmp',
58 'image/x-ms-bmp',
59 'image/png',
60 'image/jpeg',
61 'image/jpg',
62 'image/webp'
63 );
64 $disable_image_conversion = false;
65 if ( in_array( $upload['type'], $applicable_mime_types ) ) {
66 // Exlude from conversion and resizing images with filenames ending with '-nr', e.g. birds-nr.png
67 if ( false !== strpos( $upload['file'], '-nr.' ) ) {
68 return $upload;
69 }
70 // Image conversion is not disabled
71 if ( !$disable_image_conversion ) {
72 // Convert BMP
73 if ( 'image/bmp' === $upload['type'] || 'image/x-ms-bmp' === $upload['type'] ) {
74 $upload = $this->maybe_convert_image( 'bmp', $upload );
75 }
76 // Convert PNG without transparency
77 if ( 'image/png' === $upload['type'] ) {
78 $upload = $this->maybe_convert_image( 'png', $upload );
79 }
80 }
81 // At this point, BMPs and non-transparent PNGs are already converted to JPGs, unless excluded with '-nr' suffix.
82 // Let's perform resize operation as needed, i.e. if image dimension is larger than specified
83 $mime_types_to_resize = array(
84 'image/jpeg',
85 'image/jpg',
86 'image/png',
87 'image/webp'
88 );
89 if ( !is_wp_error( $upload ) && in_array( $upload['type'], $mime_types_to_resize ) && filesize( $upload['file'] ) > 0 ) {
90 // https://developer.wordpress.org/reference/classes/wp_image_editor/
91 $wp_image_editor = wp_get_image_editor( $upload['file'] );
92 if ( !is_wp_error( $wp_image_editor ) ) {
93 $image_size = $wp_image_editor->get_size();
94 $max_width = $options['image_max_width'];
95 $max_height = $options['image_max_height'];
96 $convert_to_jpg_quality = 82;
97 // Check upload image's dimension and only resize if larger than the defined max dimension
98 if ( isset( $image_size['width'] ) && $image_size['width'] > $max_width || isset( $image_size['height'] ) && $image_size['height'] > $max_height ) {
99 $wp_image_editor->resize( $max_width, $max_height, false );
100 // false is for no cropping
101 }
102 // Save
103 if ( 'image/jpg' === $upload['type'] || 'image/jpeg' === $upload['type'] ) {
104 $wp_image_editor->set_quality( $convert_to_jpg_quality );
105 }
106 $wp_image_editor->save( $upload['file'] );
107 }
108 }
109 }
110 return $upload;
111 }
112
113 /**
114 * Convert BMP or PNG without transparency into JPG
115 *
116 * @since 4.3.0
117 */
118 public function maybe_convert_image( $file_extension, $upload ) {
119 $image_object = null;
120 // Get image object from uploaded BMP/PNG
121 if ( 'bmp' === $file_extension ) {
122 if ( is_file( $upload['file'] ) ) {
123 // Generate image object from BMP for conversion to JPG later
124 if ( function_exists( 'imagecreatefrombmp' ) ) {
125 // PHP >= v7.2
126 $image_object = imagecreatefrombmp( $upload['file'] );
127 } else {
128 // PHP < v7.2
129 require_once ASENHA_PATH . 'includes/bmp-to-image-object.php';
130 $image_object = bmp_to_image_object( $upload['file'] );
131 }
132 }
133 }
134 if ( 'png' === $file_extension ) {
135 // Detect alpha/transparency in PNG
136 $this->png_is_transparent = false;
137 if ( is_file( $upload['file'] ) ) {
138 if ( function_exists( 'imagecreatefrompng' ) ) {
139 // GD library is present, so 'imagecreatefrompng' function is available
140 // Generate image object from PNG for potential conversion to JPG later.
141 $image_object = imagecreatefrompng( $upload['file'] );
142 // Get image dimension
143 list( $width, $height ) = getimagesize( $upload['file'] );
144 // Run through pixels until transparent pixel is found
145 for ($x = 0; $x < $width; $x++) {
146 for ($y = 0; $y < $height; $y++) {
147 $pixel_color_index = imagecolorat( $image_object, $x, $y );
148 $pixel_rgba = imagecolorsforindex( $image_object, $pixel_color_index );
149 // array of red, green, blue and alpha values
150 if ( $pixel_rgba['alpha'] > 0 ) {
151 // a pixel with alpha/transparency has been found
152 // alpha value range from 0 (completely opaque) to 127 (fully transparent).
153 // Ref: https://www.php.net/manual/en/function.imagecolorallocatealpha.php
154 $this->png_is_transparent = true;
155 break 2;
156 // Break both 'for' loops
157 }
158 }
159 }
160 } else {
161 if ( class_exists( 'Imagick' ) ) {
162 $imagick = new Imagick();
163 $imagick->readImage( $upload['file'] );
164 // Ref: https://stackoverflow.com/a/52295997
165 // Ref: https://www.php.net/manual/en/imagick.getimagechannelrange.php
166 // If the channel is defined, and has any transparent areas across any frame, then maxima will always be greater then minima.
167 // 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.
168 $alpha_range = $imagick->getImageChannelRange( Imagick::CHANNEL_ALPHA );
169 $this->png_is_transparent = $alpha_range['minima'] < $alpha_range['maxima'];
170 }
171 }
172 }
173 // Do not convert PNG with alpha/transparency
174 if ( $this->png_is_transparent ) {
175 return $upload;
176 }
177 }
178 // Let's convert BMP and non-transparent PNG into JPG
179 $converted_to_jpg = false;
180 if ( is_object( $image_object ) || class_exists( 'Imagick' ) ) {
181 $wp_uploads = wp_upload_dir();
182 $old_filename = wp_basename( $upload['file'] );
183 // Assign new, unique file name for the converted image
184 // $new_filename = wp_basename( str_ireplace( '.' . $file_extension, '.jpg', $old_filename ) );
185 $new_filename = str_ireplace( '.' . $file_extension, '.jpg', $old_filename );
186 $new_filename = wp_unique_filename( dirname( $upload['file'] ), $new_filename );
187 // original image is always deleted in ASE Free
188 $keep_original_image = false;
189 $converted_to_jpg = false;
190 }
191 if ( is_object( $image_object ) ) {
192 // When image object creation is successful
193 // When conversion from BMP/PNG to JPG is successful using GD. Last parameter is JPG quality (0-100).
194 if ( imagejpeg( $image_object, $wp_uploads['path'] . '/' . $new_filename, 90 ) ) {
195 $converted_to_jpg = true;
196 }
197 } else {
198 // 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.
199 if ( class_exists( 'Imagick' ) ) {
200 $imagick = new Imagick();
201 $imagick->readImage( $upload['file'] );
202 $imagick->setImageCompressionQuality( 90 );
203 $imagick->setImageFormat( 'jpg' );
204 // $imagick->setFormat( 'jpg' );
205 if ( $imagick->writeImage( $wp_uploads['path'] . '/' . $new_filename ) ) {
206 $converted_to_jpg = true;
207 }
208 // Clear the Imagick object
209 $imagick->clear();
210 $imagick->destroy();
211 }
212 }
213 if ( $converted_to_jpg ) {
214 // Delete original BMP / PNG
215 if ( !$keep_original_image ) {
216 unlink( $upload['file'] );
217 }
218 // Add converted JPG info into $upload
219 $upload['file'] = $wp_uploads['path'] . '/' . $new_filename;
220 $upload['url'] = $wp_uploads['url'] . '/' . $new_filename;
221 $upload['type'] = 'image/jpeg';
222 }
223 return $upload;
224 }
225
226 /**
227 * Generate image object from PNG/JPG with GD library
228 *
229 * @since 6.9.11
230 */
231 public function gd_generate_webp(
232 $file,
233 $file_extension,
234 $webp_path,
235 $webp_conversion_quality
236 ) {
237 if ( 'png' == $file_extension ) {
238 $image_object = imagecreatefrompng( $file );
239 if ( $this->png_is_transparent ) {
240 imagepalettetotruecolor( $image_object );
241 }
242 }
243 if ( 'jpg' == $file_extension || 'jpeg' == $file_extension ) {
244 $image_object = imagecreatefromjpeg( $file );
245 }
246 // When creation of image object from PNG/JPG is successful. let's generate WebP image
247 // Second parameter is file path, last parameter is WebP quality (0-100).
248 if ( !is_null( $image_object ) && is_object( $image_object ) ) {
249 imagewebp( $image_object, $webp_path, $webp_conversion_quality );
250 }
251 }
252
253 /**
254 * Checks the filename before it is uploaded to WordPress and
255 * runs the fix_image_orientation function in case its needed.
256 *
257 * @since 7.5.0
258 * @link https://plugins.trac.wordpress.org/browser/fix-image-rotation/tags/2.2.2/includes/class-fix-image-rotation.php#L172
259 *
260 * @access public
261 *
262 * @hook wp_handle_upload_prefilter
263 *
264 * @param array $file An array of data for a single file.
265 *
266 * @return array An array of data for a single file.
267 */
268 public function prefilter_maybe_fix_image_orientation( $file ) {
269 // Get the file extension
270 // $suffix = substr( $file['name'], strrpos( $file['name'], '.', -1 ) + 1 );
271 $suffix = pathinfo( $file['name'], PATHINFO_EXTENSION );
272 if ( in_array( strtolower( $suffix ), array('jpg', 'jpeg', 'tiff'), true ) ) {
273 $this->fix_image_orientation( $file['tmp_name'] );
274 }
275 return $file;
276 }
277
278 /**
279 * Checks the filename before it is uploaded to WordPress and
280 * runs the fix_image_orientation function in case its needed.
281 *
282 * @since 7.5.0
283 * @link https://plugins.trac.wordpress.org/browser/fix-image-rotation/tags/2.2.2/includes/class-fix-image-rotation.php#L150
284 *
285 * @access public
286 *
287 * @hook wp_handle_upload
288 *
289 * @param array $file {
290 * Array of upload data.
291 *
292 * @type string $file Filename of the newly-uploaded file.
293 * @type string $url URL of the uploaded file.
294 * @type string $type File type.
295 * }
296 *
297 * @return array Array of upload data.
298 */
299 public function maybe_fix_image_orientation( $file ) {
300 $suffix = substr( $file['file'], strrpos( $file['file'], '.', -1 ) + 1 );
301 if ( in_array( strtolower( $suffix ), array('jpg', 'jpeg', 'tiff'), true ) ) {
302 $this->fix_image_orientation( $file['file'] );
303 }
304 return $file;
305 }
306
307 /**
308 * Fixes the orientation of the image based on exif data
309 *
310 * @since 7.5.0
311 * @link https://plugins.trac.wordpress.org/browser/fix-image-rotation/tags/2.2.2/includes/class-fix-image-rotation.php#L191
312 *
313 * @access public
314 *
315 * @param string $file Path of the file.
316 *
317 * @return void
318 */
319 public function fix_image_orientation( $file ) {
320 if ( !isset( $this->orientation_fixed[$file] ) ) {
321 $exif = @exif_read_data( $file );
322 if ( isset( $exif ) && isset( $exif['Orientation'] ) && $exif['Orientation'] > 1 ) {
323 // Need it so that image editors are available to us.
324 // include_once ABSPATH . 'wp-admin/includes/image-edit.php';
325 // Calculate the operations we need to perform on the image.
326 $operations = $this->calculate_flip_and_rotate( $file, $exif );
327 if ( false !== $operations ) {
328 // Lets flip flop and rotate the image as needed.
329 $this->do_flip_and_rotate( $file, $operations );
330 }
331 }
332 }
333 }
334
335 /**
336 * Calculate the flips and rotations image will need to do to fix its orientation.
337 *
338 * @since 7.5.0
339 * @link https://plugins.trac.wordpress.org/browser/fix-image-rotation/tags/2.2.2/includes/class-fix-image-rotation.php#L225
340 *
341 * @access private
342 *
343 * @param string $file Path of the file.
344 *
345 * @param array $exif Exif data of the image.
346 *
347 * @return array|bool Array of operations to be performed on the image,
348 * false if no operations are needed.
349 */
350 private function calculate_flip_and_rotate( $file, $exif ) {
351 $rotator = false;
352 $flipper = false;
353 $orientation = 0;
354 // Lets switch to the orientation defined in the exif data.
355 switch ( $exif['Orientation'] ) {
356 case 1:
357 // We don't want to fix an already correct image :).
358 $this->orientation_fixed[$file] = true;
359 return false;
360 case 2:
361 $flipper = array(false, true);
362 break;
363 case 3:
364 $orientation = -180;
365 $rotator = true;
366 break;
367 case 4:
368 $flipper = array(true, false);
369 break;
370 case 5:
371 $orientation = -90;
372 $rotator = true;
373 $flipper = array(false, true);
374 break;
375 case 6:
376 $orientation = -90;
377 $rotator = true;
378 break;
379 case 7:
380 $orientation = -270;
381 $rotator = true;
382 $flipper = array(false, true);
383 break;
384 case 8:
385 case 9:
386 $orientation = -270;
387 $rotator = true;
388 break;
389 default:
390 $orientation = 0;
391 $rotator = true;
392 break;
393 }
394 return compact( 'orientation', 'rotator', 'flipper' );
395 }
396
397 /**
398 * Flips and rotates the image based on the parameters provided.
399 *
400 * @since 7.5.0
401 * @link https://plugins.trac.wordpress.org/browser/fix-image-rotation/tags/2.2.2/includes/class-fix-image-rotation.php#L299
402 *
403 * @access private
404 *
405 * @param string $file Path of the file.
406 *
407 * @param array $operations {
408 * Array of operations to be performed on the image.
409 *
410 * @type bool $rotator Whether to rotate the image or not.
411 * @type int $orientation Amount of rotation to be performed in degrees.
412 * @type array|bool $flipper {
413 * Whether to flip the image or not, false if no flipping needed.
414 *
415 * @type bool $0 Flip along Horizontal Axis.
416 * @type bool $1 Flip along Vertical Axis.
417 * }
418 * }
419 *
420 * @return bool Returns true if operations were successful, false otherwise.
421 */
422 private function do_flip_and_rotate( $file, $operations ) {
423 $editor = wp_get_image_editor( $file );
424 // If GD Library is being used, then we need to store metadata to restore later.
425 if ( 'WP_Image_Editor_GD' === get_class( $editor ) ) {
426 include_once ABSPATH . 'wp-admin/includes/image.php';
427 $this->previous_meta[$file] = wp_read_image_metadata( $file );
428 }
429 if ( !is_wp_error( $editor ) ) {
430 // Lets rotate and flip the image based on exif orientation.
431 if ( true === $operations['rotator'] ) {
432 $editor->rotate( $operations['orientation'] );
433 }
434 if ( false !== $operations['flipper'] ) {
435 $editor->flip( $operations['flipper'][0], $operations['flipper'][1] );
436 }
437 $editor->save( $file );
438 $this->orientation_fixed[$file] = true;
439 add_filter(
440 'wp_read_image_metadata',
441 array($this, 'restore_meta_data'),
442 10,
443 2
444 );
445 return true;
446 }
447 return false;
448 }
449
450 /**
451 * Restores the meta data of the image after being processed.
452 *
453 * WordPress' Imagick Library does not need this, but GD library
454 * removes metadata from the image upon rotation or flip so this
455 * method restores those values.
456 *
457 * @since 7.5.0
458 * @link https://plugins.trac.wordpress.org/browser/fix-image-rotation/tags/2.2.2/includes/class-fix-image-rotation.php#L341
459 *
460 * @hook wp_read_image_metadata
461 *
462 * @param array $meta Image meta data.
463 * @param string $file Path to image file.
464 *
465 * @return array Image meta data.
466 */
467 public function restore_meta_data( $meta, $file ) {
468 if ( isset( $this->previous_meta[$file] ) ) {
469 $meta = $this->previous_meta[$file];
470 // Setting the Orientation meta to the new value after fixing the rotation.
471 $meta['orientation'] = 1;
472 return $meta;
473 }
474 return $meta;
475 }
476
477 }
478