PluginProbe
Imagify Image Optimization: Optimize Images | Compress & Convert to WebP/AVIF / trunk
Imagify Image Optimization: Optimize Images | Compress & Convert to WebP/AVIF vtrunk
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 / inc / common / attachments.php

attachments.php in Imagify Image Optimization: Optimize Images | Compress & Convert to WebP/AVIF trunk, at inc/3rd-party/nextgen-gallery/inc/common/attachments.php

358 lines 10.8 KB
No matching file
Up and down to move Enter to open Esc to close
Raw Download Zip
1 <?php
2 use Imagify\Optimization\File;
3 use Imagify\ThirdParty\NGG;
4
5 defined( 'ABSPATH' ) || exit;
6
7 add_action( 'ngg_after_new_images_added', '_imagify_ngg_optimize_attachment', IMAGIFY_INT_MAX, 2 );
8 /**
9 * Auto-optimize when a new attachment is added to the database (NGG plugin's table), except for images imported from the library.
10 *
11 * @since 1.5
12 * @author Jonathan Buttigieg
13 *
14 * @param int $gallery_id A Gallery ID.
15 * @param array $image_ids An array of Ids or objects. Ids which are sucessfully added.
16 */
17 function _imagify_ngg_optimize_attachment( $gallery_id, $image_ids ) {
18
19 if ( ! Imagify_Requirements::is_api_key_valid() || ! get_imagify_option( 'auto_optimize' ) ) {
20 return;
21 }
22
23 $is_maybe_library_import = ! empty( $_POST['action'] ) && 'import_media_library' === $_POST['action'] && ! empty( $_POST['attachment_ids'] ) && is_array( $_POST['attachment_ids'] ); // WPCS: CSRF ok.
24
25 if ( $is_maybe_library_import && ! empty( $_POST['nextgen_upload_image_sec'] ) ) { // WPCS: CSRF ok.
26 /**
27 * The images are imported from the library.
28 * In this case, those images are dealt with in _imagify_ngg_media_library_imported_image_data().
29 */
30 return;
31 }
32
33 if ( $is_maybe_library_import && ( ! empty( $_POST['gallery_id'] ) || ! empty( $_POST['gallery_name'] ) ) ) { // WPCS: CSRF ok.
34 /**
35 * Same thing but for NGG 2.0 probably.
36 */
37 return;
38 }
39
40 foreach ( $image_ids as $image ) {
41 if ( is_numeric( $image ) ) {
42 $image_id = (int) $image;
43 } elseif ( is_object( $image ) && ! empty( $image->pid ) ) {
44 $image_id = (int) $image->pid;
45 } else {
46 $image_id = 0;
47 }
48
49 if ( ! $image_id ) {
50 continue;
51 }
52
53 /**
54 * Allow to prevent automatic optimization for a specific NGG gallery image.
55 *
56 * @since 1.6.12
57 * @author Grégory Viguier
58 *
59 * @param bool $optimize True to optimize, false otherwise.
60 * @param int $image_id Image ID.
61 * @param int $gallery_id Gallery ID.
62 */
63 $optimize = apply_filters( 'imagify_auto_optimize_ngg_gallery_image', true, $image_id, $gallery_id );
64
65 if ( ! $optimize ) {
66 continue;
67 }
68
69 $process = imagify_get_optimization_process( $image, 'ngg' );
70
71 if ( ! $process->is_valid() ) {
72 continue;
73 }
74
75 if ( $process->get_data()->get_optimization_status() ) {
76 // Optimization already attempted.
77 continue;
78 }
79
80 $process->optimize();
81 }
82 }
83
84 add_filter( 'ngg_medialibrary_imported_image', '_imagify_ngg_media_library_imported_image_data', 10, 2 );
85 /**
86 * Import Imagify data from a WordPress image to a new NGG image, and optimize the thumbnails.
87 *
88 * @since 1.5
89 * @author Jonathan Buttigieg
90 *
91 * @param object $image A NGG image object.
92 * @param object $attachment An attachment object.
93 * @return object
94 */
95 function _imagify_ngg_media_library_imported_image_data( $image, $attachment ) {
96 $wp_process = imagify_get_optimization_process( $attachment->ID, 'wp' );
97
98 if ( ! $wp_process->is_valid() || ! $wp_process->get_media()->is_supported() ) {
99 return $image;
100 }
101
102 $wp_data = $wp_process->get_data();
103
104 if ( ! $wp_data->is_optimized() ) {
105 // The main image is not optimized.
106 return $image;
107 }
108
109 // Copy the full size data.
110 $wp_full_size_data = $wp_data->get_size_data();
111 $optimization_level = $wp_data->get_optimization_level();
112
113 NGG\DB::get_instance()->update(
114 $image->pid,
115 [
116 'pid' => $image->pid,
117 'optimization_level' => $optimization_level,
118 'status' => $wp_data->get_optimization_status(),
119 'data' => [
120 'sizes' => [
121 'full' => $wp_full_size_data,
122 ],
123 'stats' => [
124 'original_size' => $wp_full_size_data['original_size'],
125 'optimized_size' => $wp_full_size_data['optimized_size'],
126 'percent' => $wp_full_size_data['percent'],
127 ],
128 ],
129 ]
130 );
131
132 $ngg_process = imagify_get_optimization_process( $image->pid, 'ngg' );
133
134 if ( ! $ngg_process->is_valid() ) {
135 // WTF.
136 return $image;
137 }
138
139 // Copy the backup file (we don't want to backup the optimized file if it can be avoided).
140 $ngg_media = $ngg_process->get_media();
141 $wp_media = $wp_process->get_media();
142 $wp_backup_path = $wp_media->get_backup_path();
143 $filesystem = imagify_get_filesystem();
144 $backup_copied = false;
145
146 if ( $wp_backup_path ) {
147 $ngg_backup_path = $ngg_media->get_raw_backup_path();
148 $backup_copied = $filesystem->copy( $wp_backup_path, $ngg_backup_path, true );
149
150 if ( $backup_copied ) {
151 $filesystem->chmod_file( $ngg_backup_path );
152 }
153 }
154
155 /**
156 * Next-gen for the full size.
157 * Look for an existing copy locally:
158 * - if it exists, copy it (and its optimization data),
159 * - if not, add it to the optimization queue.
160 */
161 $add_full_nextgen = $wp_media->is_image();
162
163 if ( $add_full_nextgen ) {
164 $formats = [
165 'avif' => $wp_process::AVIF_SUFFIX,
166 'webp' => $wp_process::WEBP_SUFFIX,
167 ];
168
169 foreach ( $formats as $extension => $suffix ) {
170 $wp_full_path_nextgen = false;
171 $nextgen_size_name = 'full' . $suffix;
172 $wp_nextgen_data = $wp_data->get_size_data( $nextgen_size_name );
173
174 // Get the path to the next-gen image if it exists.
175 $wp_full_path_nextgen = $wp_process->get_fullsize_file()->get_path_to_nextgen( $extension );
176
177 if ( $wp_full_path_nextgen && ! $filesystem->exists( $wp_full_path_nextgen ) ) {
178 $wp_full_path_nextgen = false;
179 }
180
181 if ( $wp_full_path_nextgen ) {
182 // We know we have a next-gen version. Make sure we have the right data.
183 $wp_nextgen_data['success'] = true;
184
185 if ( empty( $wp_nextgen_data['original_size'] ) ) {
186 // The next-gen data is missing.
187 $full_size_weight = $wp_full_size_data['original_size'];
188
189 if ( ! $full_size_weight && $wp_backup_path ) {
190 // For some reason we don't have the original file weight, but we can get it from the backup file.
191 $full_size_weight = $filesystem->size( $wp_backup_path );
192
193 if ( $full_size_weight ) {
194 $wp_nextgen_data['original_size'] = $full_size_weight;
195 }
196 }
197 }
198
199 if ( ! empty( $wp_nextgen_data['original_size'] ) && empty( $wp_nextgen_data['optimized_size'] ) ) {
200 // The next-gen file size.
201 $wp_nextgen_data['optimized_size'] = $filesystem->size( $wp_full_path_nextgen );
202 }
203
204 if ( empty( $wp_nextgen_data['original_size'] ) || empty( $wp_nextgen_data['optimized_size'] ) ) {
205 // We must have both original and optimized sizes.
206 $wp_nextgen_data = [];
207 }
208 }
209
210 if ( $wp_full_path_nextgen && $wp_nextgen_data ) {
211 // We have the file and the data.
212 // Copy the file.
213 $ngg_full_file = new File( $ngg_media->get_raw_fullsize_path() );
214 $ngg_full_path_nextgen = $ngg_full_file->get_path_to_nextgen( $extension ); // Destination.
215
216 if ( $ngg_full_path_nextgen ) {
217 $copied = $filesystem->copy( $wp_full_path_nextgen, $ngg_full_path_nextgen, true );
218
219 if ( $copied ) {
220 // Success.
221 $filesystem->chmod_file( $ngg_full_path_nextgen );
222 $add_full_nextgen = false;
223 }
224 }
225
226 if ( ! $add_full_nextgen ) {
227 // The next-gen file has been successfully copied: now, copy the data.
228 $ngg_process->get_data()->update_size_optimization_data( $nextgen_size_name, $wp_nextgen_data );
229 }
230 }
231 }
232 }
233
234 // Optimize thumbnails.
235 $sizes = $ngg_media->get_media_files();
236 unset( $sizes['full'] );
237
238 if ( $add_full_nextgen ) {
239 // We could not use a local next-gen copy: ask for a new one.
240
241 $formats = imagify_nextgen_images_formats();
242
243 foreach ( $formats as $format ) {
244 if ( 'webp' === $format ) {
245 $suffix = $wp_process::WEBP_SUFFIX;
246 } elseif ( 'avif' === $format ) {
247 $suffix = $wp_process::AVIF_SUFFIX;
248 }
249
250 $sizes[ 'full' . $suffix ] = [];
251 }
252 }
253
254 if ( ! $sizes ) {
255 return $image;
256 }
257
258 $args = [
259 'hook_suffix' => 'optimize_imported_images',
260 ];
261
262 $ngg_process->optimize_sizes( array_keys( $sizes ), $optimization_level, $args );
263
264 return $image;
265 }
266
267 add_action( 'ngg_generated_image', 'imagify_ngg_maybe_add_dynamic_thumbnail_to_background_process', IMAGIFY_INT_MAX, 2 );
268 /**
269 * Add a dynamically generated thumbnail to the background process queue.
270 * Note that this won't work when images are imported (from WP Library or uploaded), since they are already being processed, and locked.
271 *
272 * @since 1.8
273 * @since 1.9 Doesn't use the class Imagify_NGG_Dynamic_Thumbnails_Background_Process anymore.
274 * @author Grégory Viguier
275 *
276 * @param object $image A NGG image object.
277 * @param string $size The thumbnail size name.
278 */
279 function imagify_ngg_maybe_add_dynamic_thumbnail_to_background_process( $image, $size ) {
280 NGG\DynamicThumbnails::get_instance()->push_to_queue( $image, $size );
281 }
282
283 add_action( 'ngg_delete_picture', 'imagify_ngg_cleanup_after_media_deletion', 10, 2 );
284 /**
285 * Delete everything when a NGG image is deleted.
286 *
287 * @since 1.9
288 * @author Grégory Viguier
289 *
290 * @param int $image_id The image ID.
291 * @param object $image A NGG object.
292 */
293 function imagify_ngg_cleanup_after_media_deletion( $image_id, $image ) {
294 $process = imagify_get_optimization_process( $image, 'ngg' );
295
296 if ( ! $process->is_valid() ) {
297 return;
298 }
299
300 // Trigger a common hook.
301 imagify_trigger_delete_media_hook( $process );
302
303 /**
304 * The backup file has already been deleted by NGG.
305 * Delete the next-gen versions and the optimization data.
306 */
307 $process->delete_nextgen_files();
308
309 $process->get_data()->delete_optimization_data();
310 }
311
312 add_filter( 'imagify_crop_thumbnail', 'imagify_ngg_should_crop_thumbnail', 10, 4 );
313 /**
314 * In case of a dynamic thumbnail, tell if the image must be croped or resized.
315 *
316 * @since 1.9
317 * @author Grégory Viguier
318 *
319 * @param bool $crop True to crop the thumbnail, false to resize. Null by default.
320 * @param string $size Name of the thumbnail size.
321 * @param array $size_data Data of the thumbnail being processed. Contains at least 'width', 'height', and 'path'.
322 * @param MediaInterface $media The MediaInterface instance corresponding to the image being processed.
323 * @return bool
324 */
325 function imagify_ngg_should_crop_thumbnail( $crop, $size, $size_data, $media ) {
326 static $data_per_media = [];
327 static $storage_per_media = [];
328
329 if ( 'ngg' !== $media->get_context() ) {
330 return $crop;
331 }
332
333 $media_id = $media->get_id();
334
335 if ( ! isset( $data_per_media[ $media_id ] ) ) {
336 $image = \nggdb::find_image( $media_id );
337
338 if ( ! empty( $image->_ngiw ) ) {
339 $storage_per_media[ $media_id ] = $image->_ngiw->get_storage()->object;
340 } else {
341 // C_Gallery_Storage is part of the NGG v3 POPE framework and is absent on v4.
342 // @codeCoverageIgnoreStart — requires live NGG POPE classes; not available in unit tests.
343 if ( ! imagify_ngg_has_pope_storage() ) {
344 return $crop;
345 }
346
347 $storage_per_media[ $media_id ] = \C_Gallery_Storage::get_instance()->object;
348 // @codeCoverageIgnoreEnd
349 }
350
351 $data_per_media[ $media_id ] = $storage_per_media[ $media_id ]->_image_mapper->find( $media_id ); // stdClass Object.
352 }
353
354 $params = $storage_per_media[ $media_id ]->get_image_size_params( $data_per_media[ $media_id ], $size );
355
356 return ! empty( $params['crop'] );
357 }
358