PluginProbe
FluentCommunity – Ultra-Fast High-Performance Social Network, Community, LMS & Online Courses / 2.7.5
FluentCommunity – Ultra-Fast High-Performance Social Network, Community, LMS & Online Courses v2.7.5
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.7.5, at app/Services/UploadHelper.php

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