PluginProbe
FluentCommunity – Ultra-Fast High-Performance Social Network, Community, LMS & Online Courses / 2.10.01
FluentCommunity – Ultra-Fast High-Performance Social Network, Community, LMS & Online Courses v2.10.01
2.10.0 2.10.01 2.9.1 2.9.0 2.8.1 2.8.0 2.7.7 2.7.5 2.7.0 2.6.01 2.6.0 2.5.0 2.4.01 trunk 1.0.90 1.0.91 1.0.92 1.0.93 1.0.94 1.0.95 1.0.96 1.0.97 1.0.98 1.0.99 1.1.0 All 77 releases
fluent-community / app / Services / UploadHelper.php

UploadHelper.php in FluentCommunity – Ultra-Fast High-Performance Social Network, Community, LMS & Online Courses 2.10.01, at app/Services/UploadHelper.php

330 lines 12.8 KB
No matching file
Up and down to move Enter to open Esc to close
Raw Download Zip
1 <?php
2
3 namespace FluentCommunity\App\Services;
4
5 use FluentCommunity\App\Models\Media;
6 use FluentCommunity\App\Services\Libs\FileSystem;
7 use FluentCommunity\Framework\Support\Arr;
8 use FluentCommunity\Framework\Validator\Validator;
9
10 class UploadHelper
11 {
12 public function processUpload($requestFiles, $options = [])
13 {
14 $defaultOptions = [
15 'max_size' => 10,
16 'size_unit' => 'MB',
17 'disable_convert' => 'no',
18 'resize' => true,
19 'max_width' => 0,
20 'context' => '',
21 'skip_convert' => ''
22 ];
23
24 $options = wp_parse_args($options, $defaultOptions);
25
26 $allowedTypes = implode(
27 ',',
28 apply_filters('fluent_community/support_attachment_types', [
29 'image/jpeg',
30 'image/pjpeg',
31 'image/jpeg',
32 'image/pjpeg',
33 'image/png',
34 'image/gif',
35 'image/webp'
36 ])
37 );
38
39 $maxFileUnit = apply_filters('fluent_community/media_upload_max_file_unit', $options['size_unit']);
40 $maxFileSize = apply_filters('fluent_community/media_upload_max_file_size', $options['max_size']);
41
42 $allowedFileSize = $maxFileSize;
43 if (strtoupper($maxFileUnit) == 'MB') {
44 $allowedFileSize = $maxFileSize * 1024;
45 } else if (strtoupper($maxFileUnit) == 'GB') {
46 $allowedFileSize = $maxFileSize * 1024 * 1024;
47 }
48
49 // messages belong on the constructor: Validator::validate() takes only data and rules
50 $validator = Validator::make($requestFiles, [
51 'file' => 'required|mimetypes:' . $allowedTypes . '|max:' . $allowedFileSize,
52 ], [
53 'file.required' => __('No upload file was received. Please try again.', 'fluent-community'),
54 'file.mimetypes' => __('The file must be an image type.', 'fluent-community'),
55 /* translators: %$1s is replaced by the maximum allowed file size, %2$s is replaced by the file size unit (e.g. MB) */
56 'file.max' => sprintf(__('The file size must be less than %1$s%2$s.', 'fluent-community'), $maxFileSize, $maxFileUnit)
57 ]);
58
59 if ($validator->fails()) {
60 return new \WP_Error('validation_error', __('Validation Error', 'fluent-community'), $validator->errors());
61 }
62
63 add_filter('wp_handle_upload', [self::class, 'fixImageOrientation']);
64 $uploadedFiles = FileSystem::put($requestFiles);
65 remove_filter('wp_handle_upload', [self::class, 'fixImageOrientation']);
66
67 $file = Arr::get($uploadedFiles, 0);
68
69 if (is_wp_error($file)) {
70 return $file;
71 }
72
73 if (!is_array($file) || empty($file['url']) || empty($file['file']) || empty($file['type'])) {
74 return new \WP_Error('upload_error', __('No upload file was received. Please try again.', 'fluent-community'));
75 }
76
77 $upload_dir = wp_upload_dir();
78
79 $originalUrl = $file['url'];
80 $orginalPath = $upload_dir['basedir'] . '/fluent-community/' . $file['file'];
81 $originalFileType = $file['type'];
82 $originalFileName = $file['file'];
83
84 $willWebPConvert = Arr::get($options, 'disable_convert') != 'yes';
85
86 $willWebPConvert = apply_filters('fluent_community/convert_image_to_webp', $willWebPConvert, $file);
87 $willResize = Arr::get($options, 'resize') != 'yes';
88 $maxWidth = Arr::get($options, 'max_width', 0);
89
90 $willResize = apply_filters('fluent_community/media_upload_resize', $willResize, $file);
91
92 if ($context = Arr::get($options, 'context')) {
93 $maxWidth = apply_filters('fluent_community/media_upload_max_width_' . $context, $maxWidth, $file);
94 }
95
96 if ($willResize && $maxWidth) {
97 $upload_dir = wp_upload_dir();
98 $fileUrl = str_replace($upload_dir['baseurl'], $upload_dir['basedir'], $file['url']);
99
100 $editor = wp_get_image_editor($fileUrl);
101
102 if (!is_wp_error($editor) && $editor->get_size()['width'] > $maxWidth) {
103 // Current file extension
104 $ext = pathinfo($file['url'], PATHINFO_EXTENSION);
105 $imageExtensions = ['jpg', 'jpeg', 'png', 'gif'];
106 $willConvert = in_array($ext, $imageExtensions) && $willWebPConvert;
107
108 if ($willConvert) {
109 $imageExtensions = array_map(function ($ext) {
110 return '.' . $ext;
111 }, $imageExtensions);
112
113 $fileUrl = str_replace($imageExtensions, '.webp', $fileUrl);
114 $file['file'] = str_replace($imageExtensions, '.webp', $file['file']);
115 $file['url'] = str_replace($imageExtensions, '.webp', $file['url']);
116 $file['type'] = 'image/webp';
117 }
118
119 // resize the image
120 $editor->resize($maxWidth, null, false);
121 $editor->set_quality(90);
122 if ($willConvert) {
123 $result = $editor->save($fileUrl, 'image/webp');
124 if ($result['mime-type'] == 'image/webp') {
125 // remove original file now
126 wp_delete_file(str_replace('.webp', '.' . $ext, $fileUrl));
127 }
128 $file['is_converted'] = true;
129 } else {
130 $result = $editor->save($fileUrl);
131 }
132
133 if ($result['mime-type'] != 'image/webp') {
134 $file['file'] = $originalFileName;
135 $file['url'] = $originalUrl;
136 $file['type'] = $result['mime-type'];
137 }
138
139 $file['meta'] = [
140 'width' => $editor->get_size()['width'],
141 'height' => $editor->get_size()['height']
142 ];
143 }
144 $file['path'] = $upload_dir['basedir'] . '/fluent-community/' . $file['file'];
145 } else {
146 $upload_dir = wp_upload_dir();
147 $file['path'] = $upload_dir['basedir'] . '/fluent-community/' . $file['file'];
148 }
149
150 if ($willWebPConvert && empty($file['is_converted']) && !Arr::get($options, 'skip_convert')) {
151 $path = $file['path'];
152 $extension = pathinfo($path, PATHINFO_EXTENSION);
153
154 $convertFromExtensions = ['png', 'jpg', 'jpeg', 'gif'];
155 if ($extension != 'webp' && in_array($extension, $convertFromExtensions)) {
156 // Let's convert to webp
157 $editor = wp_get_image_editor($file['path']);
158 if (!is_wp_error($editor)) {
159 $file['path'] = str_replace('.' . $extension, '.webp', $file['path']);
160 $file['url'] = str_replace('.' . $extension, '.webp', $file['url']);
161 $file['type'] = 'image/webp';
162 $result = $editor->save($file['path'], 'image/webp');
163
164 if ($result['mime-type'] != 'image/webp') {
165 $file['path'] = $orginalPath;
166 $file['url'] = $originalUrl;
167 $file['type'] = $result['mime-type'];
168 } else {
169 wp_delete_file($orginalPath);
170 }
171
172 $file['meta'] = [
173 'width' => $editor->get_size()['width'],
174 'height' => $editor->get_size()['height']
175 ];
176 }
177 }
178 }
179
180 $mediaData = [
181 'media_type' => $file['type'],
182 'driver' => 'local',
183 'media_path' => $file['path'],
184 'media_url' => $file['url'],
185 'settings' => Arr::get($file, 'meta', [])
186 ];
187
188 $mediaData = apply_filters('fluent_community/media_upload_data', $mediaData, $file);
189
190 if (is_wp_error($mediaData)) {
191 return $mediaData;
192 }
193
194 if (!$mediaData) {
195 return new \WP_Error('upload_error', __('Upload cancelled by a filter', 'fluent-community'));
196 }
197
198 // Let's create the media now
199 $media = Media::create($mediaData);
200
201 $mediaUrl = $media->public_url;
202
203 $mediaUrl = add_query_arg([
204 'media_key' => $media->media_key,
205 ], $mediaUrl);
206
207 return [
208 'url' => $mediaUrl,
209 'media_key' => $media->media_key,
210 'type' => $media->media_type,
211 'width' => Arr::get($media->settings, 'width'),
212 'height' => Arr::get($media->settings, 'height')
213 ];
214 }
215
216 public static function fixImageOrientation($file)
217 {
218 // Only process JPEG images (since they typically have EXIF data)
219 $image_types = array('image/jpeg', 'image/jpg');
220 if (!in_array($file['type'], $image_types)) {
221 return $file;
222 }
223
224 // Check if the EXIF extension is available
225 if (!function_exists('exif_read_data')) {
226 return $file;
227 }
228
229 // Read EXIF data from the uploaded image
230 $exif = @exif_read_data($file['file']);
231
232 if (!$exif || !isset($exif['Orientation'])) {
233 return $file;
234 }
235
236 $orientation = $exif['Orientation'];
237
238 // Nothing to correct for the "normal" orientation
239 if ($orientation == 1) {
240 return $file;
241 }
242
243 // Bake rotation into pixels before WebP conversion strips EXIF (else 2/4/5/7 render sideways)
244 if (extension_loaded('imagick') && class_exists('Imagick')) {
245 // Use Imagick if available
246 try {
247 $image = new \Imagick($file['file']);
248 $bg = new \ImagickPixel();
249 switch ($orientation) {
250 case 2: // mirror horizontal
251 $image->flopImage();
252 break;
253 case 3: // 180°
254 $image->rotateImage($bg, 180);
255 break;
256 case 4: // mirror vertical
257 $image->flipImage();
258 break;
259 case 5: // transpose (mirror vertical + 90° clockwise)
260 $image->flipImage();
261 $image->rotateImage($bg, 90);
262 break;
263 case 6: // 90° clockwise
264 $image->rotateImage($bg, 90);
265 break;
266 case 7: // transverse (mirror horizontal + 90° clockwise)
267 $image->flopImage();
268 $image->rotateImage($bg, 90);
269 break;
270 case 8: // 90° counter-clockwise
271 $image->rotateImage($bg, -90);
272 break;
273 default:
274 $image->destroy();
275 return $file;
276 }
277 // Strip EXIF data to prevent further issues
278 $image->stripImage();
279 // Save the rotated image
280 $image->writeImage($file['file']);
281 $image->destroy();
282 } catch (\Exception $e) {
283 // Leave the original file untouched if Imagick fails
284 }
285 } elseif (function_exists('imagecreatefromjpeg')) {
286 // Use GD if Imagick is not available
287 $image = @imagecreatefromjpeg($file['file']);
288 if ($image === false) {
289 return $file;
290 }
291
292 // GD's imagerotate uses counter-clockwise angles, so -90 == 90° clockwise
293 switch ($orientation) {
294 case 2: // mirror horizontal
295 imageflip($image, IMG_FLIP_HORIZONTAL);
296 break;
297 case 3: // 180°
298 $image = imagerotate($image, 180, 0);
299 break;
300 case 4: // mirror vertical
301 imageflip($image, IMG_FLIP_VERTICAL);
302 break;
303 case 5: // transpose (mirror vertical + 90° clockwise)
304 imageflip($image, IMG_FLIP_VERTICAL);
305 $image = imagerotate($image, -90, 0);
306 break;
307 case 6: // 90° clockwise
308 $image = imagerotate($image, -90, 0);
309 break;
310 case 7: // transverse (mirror horizontal + 90° clockwise)
311 imageflip($image, IMG_FLIP_HORIZONTAL);
312 $image = imagerotate($image, -90, 0);
313 break;
314 case 8: // 90° counter-clockwise
315 $image = imagerotate($image, 90, 0);
316 break;
317 default:
318 imagedestroy($image);
319 return $file;
320 }
321
322 // Save the rotated image
323 imagejpeg($image, $file['file'], 100);
324 imagedestroy($image);
325 }
326
327 return $file;
328 }
329 }
330