PluginProbe
Imagify Image Optimization: Optimize Images | Compress & Convert to WebP/AVIF / 2.2.8
Imagify Image Optimization: Optimize Images | Compress & Convert to WebP/AVIF v2.2.8
2.3.4 2.3.3 2.3.2 2.3.1 2.3.0 2.2.9 2.2.8 trunk 1.10 1.3.3 1.3.4 1.3.5 1.3.5.1 1.3.5.2 1.3.6 1.3.6.1 1.4 1.4.1 1.4.2 1.4.3 1.4.4 1.4.5 1.4.6 1.4.7 1.5 All 103 releases
imagify / inc / 3rd-party / nextgen-gallery / classes / Media / NGG.php

NGG.php in Imagify Image Optimization: Optimize Images | Compress & Convert to WebP/AVIF 2.2.8, at inc/3rd-party/nextgen-gallery/classes/Media/NGG.php

547 lines 14.2 KB
No matching file
Up and down to move Enter to open Esc to close
Raw Download Zip
1 <?php
2 namespace Imagify\ThirdParty\NGG\Media;
3
4 use Imagify\Deprecated\Traits\Media\NGGDeprecatedTrait;
5
6 /**
7 * Media class for the medias from NextGen Gallery.
8 *
9 * @since 1.9
10 * @author Grégory Viguier
11 */
12 class NGG extends \Imagify\Media\AbstractMedia {
13 use NGGDeprecatedTrait;
14
15 /**
16 * Context (where the media "comes from").
17 *
18 * @var string
19 * @since 1.9
20 * @access protected
21 * @author Grégory Viguier
22 */
23 protected $context = 'ngg';
24
25 /**
26 * The image object.
27 *
28 * @var object A \nggImage object.
29 * @since 1.9
30 * @access protected
31 * @author Grégory Viguier
32 */
33 protected $image;
34
35 /**
36 * The storage object used by NGG.
37 *
38 * @var object A \C_Gallery_Storage object (by default).
39 * @since 1.8.
40 * @access protected
41 */
42 protected $storage;
43
44 /**
45 * The constructor.
46 *
47 * @since 1.9
48 * @access public
49 * @author Grégory Viguier
50 *
51 * @param int|\nggImage|\nggdb|\StdClass $id The NGG image ID, \nggImage object, \nggdb object, or an anonym object containing a pid property.
52 */
53 public function __construct( $id ) {
54 if ( ! static::constructor_accepts( $id ) ) {
55 parent::__construct( 0 );
56 return;
57 }
58
59 if ( is_numeric( $id ) ) {
60 $this->image = \nggdb::find_image( (int) $id );
61 $id = ! empty( $this->image->pid ) ? (int) $this->image->pid : 0;
62 } elseif ( $id instanceof \nggImage ) {
63 $this->image = $id;
64 $id = (int) $id->pid;
65 } elseif ( is_object( $id ) ) {
66 $this->image = \nggdb::find_image( (int) $id->pid );
67 $id = ! empty( $this->image->pid ) ? (int) $this->image->pid : 0;
68 } else {
69 $id = 0;
70 }
71
72 if ( ! $id ) {
73 $this->image = null;
74
75 parent::__construct( 0 );
76 return;
77 }
78
79 parent::__construct( $id );
80
81 // NGG storage.
82 if ( ! empty( $this->image->_ngiw ) ) {
83 $this->storage = $this->image->_ngiw->get_storage()->object;
84 } else {
85 $this->storage = \C_Gallery_Storage::get_instance()->object;
86 }
87
88 // Load nggAdmin class.
89 $ngg_admin_functions_path = WP_PLUGIN_DIR . '/' . NGGFOLDER . '/products/photocrati_nextgen/modules/ngglegacy/admin/functions.php';
90
91 if ( ! class_exists( 'nggAdmin' ) && $this->filesystem->exists( $ngg_admin_functions_path ) ) {
92 require_once $ngg_admin_functions_path;
93 }
94 }
95
96 /**
97 * Tell if the given entry can be accepted in the constructor.
98 *
99 * @since 1.9
100 * @access public
101 * @author Grégory Viguier
102 *
103 * @param mixed $id Whatever.
104 * @return bool
105 */
106 public static function constructor_accepts( $id ) {
107 if ( ! $id ) {
108 return false;
109 }
110
111 if ( is_numeric( $id ) || $id instanceof \nggImage ) {
112 return true;
113 }
114
115 return is_object( $id ) && ! empty( $id->pid ) && is_numeric( $id->pid );
116 }
117
118
119 /** ----------------------------------------------------------------------------------------- */
120 /** NGG SPECIFICS =========================================================================== */
121 /** ----------------------------------------------------------------------------------------- */
122
123 /**
124 * Get the NGG image object.
125 *
126 * @since 1.9
127 * @access public
128 * @author Grégory Viguier
129 *
130 * @return \nggImage
131 */
132 public function get_ngg_image() {
133 return $this->image;
134 }
135
136 /**
137 * Get the NGG storage object.
138 *
139 * @since 1.9
140 * @access public
141 * @author Grégory Viguier
142 *
143 * @return \C_Gallery_Storage
144 */
145 public function get_ngg_storage() {
146 return $this->storage;
147 }
148
149
150 /** ----------------------------------------------------------------------------------------- */
151 /** ORIGINAL FILE =========================================================================== */
152 /** ----------------------------------------------------------------------------------------- */
153
154 /**
155 * Get the original file path, even if the file doesn't exist.
156 *
157 * @since 1.9
158 * @access public
159 * @author Grégory Viguier
160 *
161 * @return string|bool The file path. False on failure.
162 */
163 public function get_raw_original_path() {
164 if ( ! $this->is_valid() ) {
165 return false;
166 }
167
168 if ( $this->get_cdn() ) {
169 return $this->get_cdn()->get_file_path( 'original' );
170 }
171
172 return ! empty( $this->image->imagePath ) ? $this->image->imagePath : false;
173 }
174
175
176 /** ----------------------------------------------------------------------------------------- */
177 /** FULL SIZE FILE ========================================================================== */
178 /** ----------------------------------------------------------------------------------------- */
179
180 /**
181 * Get the URL of the media’s full size file.
182 *
183 * @since 1.9.8
184 * @access public
185 * @author Grégory Viguier
186 *
187 * @return string|bool The file URL. False on failure.
188 */
189 public function get_fullsize_url() {
190 if ( ! $this->is_valid() ) {
191 return false;
192 }
193
194 if ( $this->get_cdn() ) {
195 return $this->get_cdn()->get_file_url();
196 }
197
198 return ! empty( $this->image->imageURL ) ? $this->image->imageURL : false;
199 }
200
201 /**
202 * Get the path to the media’s full size file, even if the file doesn't exist.
203 *
204 * @since 1.9.8
205 * @access public
206 * @author Grégory Viguier
207 *
208 * @return string|bool The file path. False on failure.
209 */
210 public function get_raw_fullsize_path() {
211 if ( ! $this->is_valid() ) {
212 return false;
213 }
214
215 if ( $this->get_cdn() ) {
216 return $this->get_cdn()->get_file_path();
217 }
218
219 return ! empty( $this->image->imagePath ) ? $this->image->imagePath : false;
220 }
221
222
223 /** ----------------------------------------------------------------------------------------- */
224 /** BACKUP FILE ============================================================================= */
225 /** ----------------------------------------------------------------------------------------- */
226
227 /**
228 * Get the backup URL, even if the file doesn't exist.
229 *
230 * @since 1.9
231 * @access public
232 * @author Grégory Viguier
233 *
234 * @return string|bool The file URL. False on failure.
235 */
236 public function get_backup_url() {
237 if ( ! $this->is_valid() ) {
238 return false;
239 }
240
241 return site_url( '/' . $this->filesystem->make_path_relative( $this->get_raw_backup_path() ) );
242 }
243
244 /**
245 * Get the backup file path, even if the file doesn't exist.
246 *
247 * @since 1.9
248 * @access public
249 * @author Grégory Viguier
250 *
251 * @return string|bool The file path. False on failure.
252 */
253 public function get_raw_backup_path() {
254 if ( ! $this->is_valid() ) {
255 return false;
256 }
257
258 return get_imagify_ngg_attachment_backup_path( $this->get_raw_original_path() );
259 }
260
261
262 /** ----------------------------------------------------------------------------------------- */
263 /** THUMBNAILS ============================================================================== */
264 /** ----------------------------------------------------------------------------------------- */
265
266 /**
267 * Create the media thumbnails.
268 *
269 * @since 1.9
270 * @access public
271 * @author Grégory Viguier
272 *
273 * @return bool|WP_Error True on success. A \WP_Error instance on failure.
274 */
275 public function generate_thumbnails() {
276 if ( ! $this->is_valid() ) {
277 return new \WP_Error( 'invalid_media', __( 'This media is not valid.', 'imagify' ) );
278 }
279
280 $image_data = $this->storage->_image_mapper->find( $this->get_id() );
281
282 if ( ! $image_data ) {
283 return new \WP_Error( 'no_ngg_image', __( 'Image not found in NextGen Gallery data.', 'imagify' ) );
284 }
285
286 if ( empty( $image_data->meta_data['backup'] ) || ! is_array( $image_data->meta_data['backup'] ) ) {
287 $full_path = $this->storage->get_image_abspath( $image_data );
288
289 $image_data->meta_data['backup'] = [
290 'filename' => $this->filesystem->file_name( $full_path ), // Yes, $full_path.
291 'width' => $image_data->meta_data['width'], // Original image width.
292 'height' => $image_data->meta_data['height'], // Original image height.
293 'generated' => microtime(),
294 ];
295 }
296
297 $backup_path = $this->storage->get_image_abspath( $image_data, 'backup' );
298 $failed = [];
299
300 foreach ( $this->get_media_files() as $size_name => $size_data ) {
301 if ( 'full' === $size_name ) {
302 continue;
303 }
304
305 $params = $this->storage->get_image_size_params( $image_data, $size_name );
306 $thumbnail = @$this->storage->generate_image_clone( // phpcs:ignore WordPress.PHP.NoSilencedErrors.Discouraged
307 $backup_path,
308 $this->storage->get_image_abspath( $image_data, $size_name ),
309 $params
310 );
311
312 if ( ! $thumbnail ) {
313 // Failed.
314 $failed[] = $size_name;
315 unset( $image_data->meta_data[ $size_name ] );
316 continue;
317 }
318
319 $size_meta = [
320 'width' => 0,
321 'height' => 0,
322 'filename' => \M_I18n::mb_basename( $thumbnail->fileName ),
323 'generated' => microtime(),
324 ];
325
326 $dimensions = $this->filesystem->get_image_size( $thumbnail->fileName );
327
328 if ( $dimensions ) {
329 $size_meta['width'] = $dimensions['width'];
330 $size_meta['height'] = $dimensions['height'];
331 }
332
333 if ( isset( $params['crop_frame'] ) ) {
334 $size_meta['crop_frame'] = $params['crop_frame'];
335 }
336
337 $image_data->meta_data[ $size_name ] = $size_meta;
338 }
339
340 // Keep our property up to date.
341 $this->image->_ngiw->_cache['meta_data'] = $image_data->meta_data;
342 $this->image->_ngiw->_orig_image = $image_data;
343
344 $post_id = $this->storage->_image_mapper->save( $image_data );
345
346 if ( ! $post_id ) {
347 return new \WP_Error( 'meta_data_not_saved', __( 'Related NextGen Gallery data could not be saved.', 'imagify' ) );
348 }
349
350 if ( $failed ) {
351 return new \WP_Error(
352 'thumbnail_restore_failed',
353 sprintf( _n( '%n thumbnail could not be restored.', '%n thumbnails could not be restored.', count( $failed ), 'imagify' ), count( $failed ) ),
354 [ 'failed_thumbnails' => $failed ]
355 );
356 }
357
358 return true;
359 }
360
361
362 /** ----------------------------------------------------------------------------------------- */
363 /** MEDIA DATA ============================================================================== */
364 /** ----------------------------------------------------------------------------------------- */
365
366 /**
367 * Tell if the current media has the required data (the data containing the file paths and thumbnails).
368 *
369 * @since 1.9
370 * @access public
371 * @author Grégory Viguier
372 *
373 * @return bool
374 */
375 public function has_required_media_data() {
376 static $sizes;
377
378 if ( ! $this->is_valid() ) {
379 return false;
380 }
381
382 if ( ! isset( $sizes ) ) {
383 $sizes = $this->get_media_files();
384 }
385
386 return $sizes && ! empty( $this->image->imagePath );
387 }
388
389 /**
390 * Get the list of the files of this media, including the full size file.
391 *
392 * @since 1.9
393 * @access public
394 * @author Grégory Viguier
395 *
396 * @return array {
397 * An array with the size names as keys ('full' is used for the full size file), and arrays of data as values:
398 *
399 * @type string $size The size name.
400 * @type string $path Absolute path to the file.
401 * @type int $width The file width.
402 * @type int $height The file height.
403 * @type string $mime-type The file mime type.
404 * @type bool $disabled True if the size is disabled in the plugin’s settings.
405 * }
406 */
407 public function get_media_files() {
408 if ( ! $this->is_valid() ) {
409 return [];
410 }
411
412 $fullsize_path = $this->get_raw_fullsize_path();
413
414 if ( ! $fullsize_path ) {
415 return [];
416 }
417
418 $dimensions = $this->get_dimensions();
419 $all_sizes = [
420 'full' => [
421 'size' => 'full',
422 'path' => $fullsize_path,
423 'width' => $dimensions['width'],
424 'height' => $dimensions['height'],
425 'mime-type' => $this->get_mime_type(),
426 'disabled' => false,
427 ],
428 ];
429
430 if ( ! $this->is_image() ) {
431 return $this->filter_media_files( $all_sizes );
432 }
433
434 // Remove common values (that have no value for us here, lol). Also remove 'full' and 'backup'.
435 $image_data = array_diff_key(
436 $this->image->meta_data,
437 [
438 'full' => 1,
439 'backup' => 1,
440 'width' => 1,
441 'height' => 1,
442 'md5' => 1,
443 'aperture' => 1,
444 'credit' => 1,
445 'camera' => 1,
446 'caption' => 1,
447 'created_timestamp' => 1,
448 'copyright' => 1,
449 'focal_length' => 1,
450 'iso' => 1,
451 'shutter_speed' => 1,
452 'flash' => 1,
453 'title' => 1,
454 'keywords' => 1,
455 'saved' => 1,
456 ]
457 );
458
459 if ( ! $image_data ) {
460 return $this->filter_media_files( $all_sizes );
461 }
462
463 $ngg_data = $this->storage->_image_mapper->find( $this->get_id() );
464
465 foreach ( $image_data as $size => $size_data ) {
466 if ( ! isset( $size_data['width'], $size_data['height'], $size_data['filename'], $size_data['generated'] ) ) {
467 continue;
468 }
469
470 $file_type = (object) wp_check_filetype( $size_data['filename'], $this->get_allowed_mime_types() );
471
472 if ( ! $file_type->type ) {
473 continue;
474 }
475
476 $all_sizes[ $size ] = [
477 'size' => $size,
478 'path' => $this->storage->get_image_abspath( $ngg_data, $size ),
479 'width' => (int) $size_data['width'],
480 'height' => (int) $size_data['height'],
481 'mime-type' => $file_type->type,
482 'disabled' => false,
483 ];
484 }
485
486 return $this->filter_media_files( $all_sizes );
487 }
488
489 /**
490 * If the media is an image, get its width and height.
491 *
492 * @since 1.9
493 * @access public
494 * @author Grégory Viguier
495 *
496 * @return array
497 */
498 public function get_dimensions() {
499 if ( ! $this->is_image() ) {
500 return [
501 'width' => 0,
502 'height' => 0,
503 ];
504 }
505
506 return [
507 'width' => ! empty( $this->image->meta_data['width'] ) ? (int) $this->image->meta_data['width'] : 0,
508 'height' => ! empty( $this->image->meta_data['height'] ) ? (int) $this->image->meta_data['height'] : 0,
509 ];
510 }
511
512 /**
513 * Update the media data dimensions.
514 *
515 * @since 1.9
516 * @access public
517 * @author Grégory Viguier
518 *
519 * @param array $dimensions {
520 * An array containing width and height.
521 *
522 * @type int $width The image width.
523 * @type int $height The image height.
524 * }
525 */
526 protected function update_media_data_dimensions( $dimensions ) {
527 $changed = false;
528 $data = [
529 'width' => $dimensions['width'],
530 'height' => $dimensions['height'],
531 'md5' => md5_file( $this->get_raw_fullsize_path() ),
532 ];
533
534 foreach ( $data as $k => $v ) {
535 if ( ! isset( $this->image->meta_data[ $k ] ) || $this->image->meta_data[ $k ] !== $v ) {
536 $this->image->meta_data[ $k ] = $v;
537
538 $changed = true;
539 }
540 }
541
542 if ( $changed ) {
543 \nggdb::update_image_meta( $this->id, $this->image->meta_data );
544 }
545 }
546 }
547