PluginProbe
Document Gallery / 3.5.3
Document Gallery v3.5.3
trunk 0.8 0.8.5 1.0 1.0.1 1.0.2 1.0.3 1.0.4 1.1 1.2 1.2.1 1.3 1.3.1 1.4 1.4.1 1.4.2 1.4.3 2.0 2.0.1 2.0.10 2.0.2 2.0.3 2.0.4 2.0.5 2.0.6 All 94 releases
document-gallery / inc / class-thumber.php

class-thumber.php in Document Gallery 3.5.3, at inc/class-thumber.php

791 lines 25.4 KB
No matching file
Up and down to move Enter to open Esc to close
Raw Download Zip
1 <?php
2 defined( 'WPINC' ) OR exit;
3
4 /**
5 * Thumber wraps the functionality required to
6 * generate thumbnails for arbitrary documents.
7 *
8 * @author drossiter
9 */
10 class DG_Thumber {
11
12 /**
13 * Returns the default mapping of thumber slug to whether it is active or not.
14 *
15 * @param $skeleton bool When true, values that require computation will be
16 * skipped. Useful when only structure of options is needed.
17 *
18 * @return array The default thumbnail generation methods.
19 */
20 public static function getDefaultThumbers( $skeleton = false ) {
21 $gs_active = $imagick_active = null;
22 if ( ! $skeleton ) {
23 $gs_active = (bool) self::getGhostscriptExecutable();
24 $imagick_active = self::isImagickAvailable();
25 }
26
27 return array(
28 'av' => true,
29 'gs' => $gs_active,
30 'imagick' => $imagick_active
31 );
32 }
33
34 /**
35 * Sets the thumbnail for the given attachment ID.
36 *
37 * @param int $ID Document ID.
38 * @param string $path System path to thumbnail.
39 * @param string $generator Descriptor for generation method -- usually method name.
40 *
41 * @return bool Whether set was successful.
42 */
43 public static function setThumbnail( $ID, $path, $generator = 'unknown' ) {
44 return self::thumbnailGenerationHarness( $generator, $ID, $path );
45 }
46
47 /**
48 * Sets the thumbnail for the given attachment ID to a failed state.
49 *
50 * @param int $ID Document ID.
51 */
52 public static function setThumbnailFailed( $ID ) {
53 $options = self::getOptions();
54 $options['thumbs'][ $ID ] = array( 'timestamp' => time() );
55 self::setOptions( $options );
56 }
57
58 /**
59 * Wraps generation of thumbnails for various attachment filetypes.
60 *
61 * @param int $ID Document ID
62 * @param int $pg Page number to get thumb from.
63 * @param bool $generate_if_missing Whether to attempt generating the thumbnail if missing.
64 *
65 * @return string URL to the thumbnail.
66 */
67 public static function getThumbnail( $ID, $pg = 1, $generate_if_missing = true ) {
68 $options = self::getOptions();
69
70 // if we haven't saved a thumb, generate one
71 if ( empty( $options['thumbs'][ $ID ] ) ) {
72 // short-circuit generation if not required
73 if ( ! $generate_if_missing ) {
74 return null;
75 }
76
77 // do the processing
78 $file = get_attached_file( $ID );
79
80 foreach ( self::getThumbers() as $ext_preg => $thumber ) {
81 $ext_preg = '!\.(?:' . $ext_preg . ')$!i';
82
83 if ( preg_match( $ext_preg, $file ) ) {
84 if ( DG_Logger::logEnabled() ) {
85 $toLog = sprintf( __( 'Attempting to generate thumbnail for attachment #%d with (%s)',
86 'document-gallery' ), $ID, is_array( $thumber ) ? implode( '::', $thumber ) : print_r( $thumber, true ) );
87 DG_Logger::writeLog( DG_LogLevel::Detail, $toLog );
88 }
89
90 if ( self::thumbnailGenerationHarness( $thumber, $ID, $pg ) ) {
91 // harness updates options so we need a new copy
92 $options = self::getOptions();
93 break;
94 }
95 }
96 }
97 }
98
99 $new = empty( $options['thumbs'][ $ID ] );
100 if ( $new || empty( $options['thumbs'][ $ID ]['thumber'] ) ) {
101 if ( $new ) {
102 self::setThumbnailFailed( $ID );
103 }
104
105 // fallback to default thumb for attachment type
106 $url = self::getDefaultThumbnail( $ID, $pg );
107 } else {
108 // use generated thumbnail
109 $url = $options['thumbs'][ $ID ]['thumb_url'];
110 }
111
112 return $url;
113 }
114
115 /*==========================================================================
116 * AUDIO VIDEO THUMBNAILS
117 *=========================================================================*/
118
119 /**
120 * Uses wp_read_video_metadata() and wp_read_audio_metadata() to retrieve
121 * an embedded image to use as a thumbnail.
122 *
123 * @param string $ID The attachment ID to retrieve thumbnail from.
124 * @param int $pg Unused.
125 *
126 * @return bool|string False on failure, URL to thumb on success.
127 */
128 public static function getAudioVideoThumbnail( $ID, $pg = 1 ) {
129 include_once DG_WPADMIN_PATH . 'includes/media.php';
130
131 $attachment = get_post( $ID );
132 $doc_path = get_attached_file( $ID );
133
134 if ( preg_match( '#^video/#', get_post_mime_type( $attachment ) ) ) {
135 $metadata = wp_read_video_metadata( $doc_path );
136 } elseif ( preg_match( '#^audio/#', get_post_mime_type( $attachment ) ) ) {
137 $metadata = wp_read_audio_metadata( $doc_path );
138 }
139
140 // unsupported mime type || no embedded image present
141 if ( ! isset( $metadata ) || empty( $metadata['image']['data'] ) ) {
142 return false;
143 }
144
145 $ext = 'jpg';
146 switch ( $metadata['image']['mime'] ) {
147 case 'image/gif':
148 $ext = 'gif';
149 break;
150 case 'image/png':
151 $ext = 'png';
152 break;
153 }
154
155 $temp_file = self::getTempFile( $ext );
156
157 if ( ! $fp = @fopen( $temp_file, 'wb' ) ) {
158 DG_Logger::writeLog( DG_LogLevel::Error, __( 'Could not open file: ', 'document-gallery' ) . $temp_file );
159
160 return false;
161 }
162
163 if ( ! @fwrite( $fp, $metadata['image']['data'] ) ) {
164 DG_Logger::writeLog( DG_LogLevel::Error, __( 'Could not write file: ', 'document-gallery' ) . $temp_file );
165 fclose( $fp );
166
167 return false;
168 }
169
170 fclose( $fp );
171
172 return $temp_file;
173 }
174
175 /**
176 * @return array All extensions supported by WP Audio Video Media metadata.
177 */
178 private static function getAudioVideoExts() {
179 return array_merge( wp_get_audio_extensions(), wp_get_video_extensions() );
180 }
181
182 /*==========================================================================
183 * IMAGICK THUMBNAILS
184 *=========================================================================*/
185
186 /**
187 * Uses WP_Image_Editor_Imagick to generate thumbnails.
188 *
189 * @param int $ID The attachment ID to retrieve thumbnail from.
190 * @param int $pg The page to get the thumbnail of.
191 *
192 * @return bool|string False on failure, URL to thumb on success.
193 */
194 public static function getImagickThumbnail( $ID, $pg = 1 ) {
195 include_once DG_PATH . 'inc/class-image-editor-imagick.php';
196
197 $doc_path = get_attached_file( $ID );
198 $img = new DG_Image_Editor_Imagick( $doc_path, $pg - 1 );
199 $err = $img->load();
200 if ( is_wp_error( $err ) ) {
201 DG_Logger::writeLog(
202 DG_LogLevel::Error,
203 __( 'Failed to open file in Imagick: ', 'document-gallery' ) .
204 $err->get_error_message() );
205
206 return false;
207 }
208
209 $temp_file = self::getTempFile();
210
211 $err = $img->save( $temp_file, 'image/png' );
212 if ( is_wp_error( $err ) ) {
213 DG_Logger::writeLog(
214 DG_LogLevel::Error,
215 __( 'Failed to save image in Imagick: ', 'document-gallery' ) .
216 $err->get_error_message() );
217
218 return false;
219 }
220
221 return $temp_file;
222 }
223
224 /**
225 * @return bool Whether WP_Image_Editor_Imagick can be used on this system.
226 */
227 public static function isImagickAvailable() {
228 static $ret = null;
229
230 if ( is_null( $ret ) ) {
231 include_once DG_WPINC_PATH . 'class-wp-image-editor.php';
232 include_once DG_WPINC_PATH . 'class-wp-image-editor-imagick.php';
233 $ret = WP_Image_Editor_Imagick::test();
234 }
235
236 return $ret;
237 }
238
239 /*==========================================================================
240 * GHOSTSCRIPT THUMBNAILS
241 *=========================================================================*/
242
243 /**
244 * Get thumbnail for document with given ID using Ghostscript. Imagick could
245 * also handle this, but is *much* slower.
246 *
247 * @param int $ID The attachment ID to retrieve thumbnail from.
248 * @param int $pg The page number to make thumbnail of -- index starts at 1.
249 *
250 * @return bool|string False on failure, URL to thumb on success.
251 */
252 public static function getGhostscriptThumbnail( $ID, $pg = 1 ) {
253 static $gs = null;
254
255 if ( is_null( $gs ) ) {
256 $options = self::getOptions();
257 $gs = $options['gs'];
258
259 if ( false !== $gs ) {
260 $gs = escapeshellarg( $gs ) . ' -sDEVICE=png16m -dFirstPage=%1$d'
261 . ' -dLastPage=%1$d -dBATCH -dNOPAUSE -dPDFFitPage -sOutputFile=%2$s %3$s 2>&1';
262 }
263 }
264
265 if ( false === $gs ) {
266 return false;
267 }
268
269 $doc_path = get_attached_file( $ID );
270 $temp_path = self::getTempFile();
271
272 exec( sprintf( $gs, $pg, $temp_path, $doc_path ), $out, $ret );
273
274 if ( $ret != 0 ) {
275 DG_Logger::writeLog( DG_LogLevel::Error, __( 'Ghostscript failed: ', 'document-gallery' ) . print_r( $out ) );
276 @unlink( $temp_path );
277
278 return false;
279 }
280
281 return $temp_path;
282 }
283
284 /**
285 * @return array All extensions supported by Ghostscript.
286 */
287 private static function getGhostscriptExts() {
288 return array( 'pdf', 'ps', 'eps' );
289 }
290
291 /**
292 * Dynamically determines whether we may call gs through exec().
293 *
294 * NOTE: This does not check the options for gs path. Don't use in
295 * thumbnail generation as it's slow and not configurable.
296 *
297 * @return bool|string If available, returns exe path. False otherwise.
298 */
299 public static function getGhostscriptExecutable() {
300 static $executable = null;
301
302 if ( is_null( $executable ) ) {
303 // we must be able to exec()
304 $executable = self::isExecAvailable();
305 if ( ! $executable ) {
306 return $executable;
307 }
308
309 // find on Windows system
310 if ( 'WIN' === strtoupper( substr( PHP_OS, 0, 3 ) ) ) {
311 // look for environment variable
312 $executable = getenv( 'GSC' );
313 if ( $executable ) {
314 return $executable;
315 }
316
317 // hope GS in the path
318 $executable = exec( 'where gswin*c.exe' );
319 if ( ! empty( $executable ) ) {
320 return $executable;
321 }
322
323 // look directly in filesystem
324 // 64- or 32-bit binary
325 $executable = exec( 'dir /o:n/s/b "C:\Program Files\gs\*gswin*c.exe"' );
326 if ( ! empty( $executable ) ) {
327 return $executable;
328 }
329
330 // 32-bit binary on 64-bit OS
331 $executable = exec( 'dir /o:n/s/b "C:\Program Files (x86)\gs\*gswin32c.exe"' );
332 $executable = empty( $executable ) ? false : $executable;
333
334 return $executable;
335 }
336
337 // handle Linux systems
338 $executable = exec( 'which gs' );
339 if ( ! empty( $executable ) ) {
340 return $executable;
341 }
342
343 // GoDaddy and others aren't setup in such a way that
344 // the above works so we need to fallback to a direct
345 // filesystem check in most common location
346 exec( 'test -e /usr/bin/gs', $dummy, $ret );
347 $executable = ( $ret === 0 ) ? '/usr/bin/gs' : false;
348
349 return $executable;
350 }
351
352 return $executable;
353 }
354
355 /**
356 * @return bool Whether we can use the GS executable.
357 */
358 public static function isGhostscriptAvailable() {
359 static $ret = null;
360
361 if ( is_null( $ret ) ) {
362 $options = self::getOptions();
363 $ret = $options['gs'] && self::isExecAvailable();
364 }
365
366 return $ret;
367 }
368
369 /*==========================================================================
370 * DEFAULT THUMBNAILS
371 *=========================================================================*/
372
373 /**
374 * Get thumbnail for document with given ID from default images.
375 *
376 * @param string $ID The attachment ID to retrieve thumbnail from.
377 * @param int $pg Unused.
378 *
379 * @return string URL to thumbnail.
380 */
381 public static function getDefaultThumbnail( $ID, $pg = 1 ) {
382 $options = self::getOptions();
383 $width = $options['width'];
384 $height = $options['height'];
385 $icon_url = DG_URL . 'assets/icons/';
386
387 // handle images
388 if ( $icon = image_downsize( $ID, array( $width, $height ) ) ) {
389 $icon = $icon[0];
390 } // default extension icon
391 elseif ( $name = self::getDefaultIcon( self::getExt( wp_get_attachment_url( $ID ) ) ) ) {
392 $icon = $icon_url . $name;
393 } // fallback to standard WP icons
394 elseif ( ! $icon = wp_mime_type_icon( $ID ) ) {
395 // everything failed. This is bad...
396 $icon = $icon_url . 'missing.png';
397 }
398
399 return $icon;
400 }
401
402 /**
403 * Returns the name of the image to represent the filetype given.
404 *
405 * @param string $ext
406 *
407 * @return string Default icon based on extension.
408 */
409 private static function getDefaultIcon( $ext ) {
410 // Maps file ext to default image name.
411 static $exts = array(
412 // Most Common First
413 'pdf' => 'pdf.png',
414 // MS Office
415 'doc|docx|docm|dotx|dotm' => 'msdoc.png',
416 'ppt|pot|pps|pptx|pptm|ppsx|ppsm|potx|potm|ppam|sldx|sldm' => 'msppt.png',
417 'xla|xls|xlt|xlw|xlsx|xlsm|xlsb|xltx|xltm|xlam' => 'msxls.png',
418 'mdb' => 'msaccess.png',
419 // iWork
420 'key' => 'key.png',
421 'numbers' => 'numbers.png',
422 'pages' => 'pages.png',
423 // Images
424 'jpg|jpeg|jpe|gif|png|bmp|tif|tiff|ico' => 'image.png',
425 // Video formats
426 'asf|asx|wmv|wmx|wm|avi|divx|flv|mov' => 'video.png',
427 'qt|mpeg|mpg|mpe|mp4|m4v|ogv|webm|mkv' => 'video.png',
428 // Audio formats
429 'mp3|m4a|m4b|ra|ram|wav|ogg|oga|wma|wax|mka' => 'audio.png',
430 'midi|mid' => 'midi.png',
431 // Text formats
432 'txt|tsv|csv' => 'text.png',
433 'rtx' => 'rtx.png',
434 'rtf' => 'rtf.png',
435 'ics' => 'ics.png',
436 'wp|wpd' => 'wordperfect.png',
437 // Programming
438 'html|htm' => 'html.png',
439 'css' => 'css.png',
440 'js' => 'javascript.png',
441 'class' => 'java.png',
442 'asc' => 'asc.png',
443 'c' => 'c.png',
444 'cc|cpp' => 'cpp.png',
445 'h' => 'h.png',
446 // Msc application formats
447 'zip|tar|gzip|gz|bz2|tgz|7z|rar' => 'compressed.png',
448 'exe' => 'exec.png',
449 'swf' => 'shockwave.png',
450 // OpenDocument formats
451 'odt' => 'opendocument-text.png',
452 'odp' => 'opendocument-presentation.png',
453 'ods' => 'opendocument-spreadsheet.png',
454 'odg' => 'opendocument-graphics.png',
455 'odb' => 'opendocument-database.png',
456 'odf' => 'opendocument-formula.png'
457 );
458
459 foreach ( $exts as $ext_preg => $icon ) {
460 $ext_preg = '!(' . $ext_preg . ')$!i';
461 if ( preg_match( $ext_preg, $ext ) ) {
462 return $icon;
463 }
464 }
465
466 return false;
467 }
468
469 /*==========================================================================
470 * GENERAL THUMBNAIL HELPER FUNCTIONS
471 *=========================================================================*/
472
473 /**
474 * @return array WP_Post objects for each attachment that has been processed.
475 */
476 public static function getThumbed() {
477 $options = self::getOptions();
478 $args = array(
479 'post_type' => 'attachment',
480 'post_status' => 'inherit',
481 'post_per_page' => - 1,
482 'post__in' => array_keys( $options['thumbs'] )
483 );
484
485 return count( $args['post__in'] ) ? get_posts( $args ) : array();
486 }
487
488 /**
489 * Key: Attachment ID
490 * Val: array
491 * + timestamp - When the thumbnail was generated (or generation failed).
492 * + thumb_path - System path to thumbnail image.
493 * + thumb_url - URL pointing to the thumbnail for this document.
494 * + thumber - Generator used to create thumb OR false if failed to gen.
495 * @return array|null Thumber options from DB or null if options not initialized.
496 */
497 public static function getOptions( $blog = null ) {
498 $options = DocumentGallery::getOptions( $blog );
499
500 return $options['thumber'];
501 }
502
503 /**
504 * Key: Attachment ID
505 * Val: array
506 * + timestamp - When the thumbnail was generated (or generation failed).
507 * + thumb_path - System path to thumbnail image.
508 * + thumb_url - URL pointing to the thumbnail for this document.
509 * + thumber - Generator used to create thumb OR false if failed to gen.
510 *
511 * @param array $options Thumber options to store in DB
512 */
513 private static function setOptions( $options, $blog = null ) {
514 $dg_options = DocumentGallery::getOptions( $blog );
515 $dg_options['thumber'] = $options;
516 DocumentGallery::setOptions( $dg_options, $blog );
517 }
518
519 /**
520 * @filter dg_thumbers Allows developers to filter the Thumbers used
521 * for specific filetypes. Index is the regex to match file extensions
522 * supported and the value is anything that can be accepted by call_user_func().
523 * The function must take two parameters, 1st is the int ID of the attachment
524 * to get a thumbnail for, 2nd is the page to take a thumbnail of
525 * (may not be relevant for some filetypes).
526 *
527 * @return array
528 */
529 private static function getThumbers() {
530 static $thumbers = null;
531
532 if ( is_null( $thumbers ) ) {
533 $options = self::getOptions();
534 $active = $options['active'];
535 $thumbers = array();
536
537 // Audio/Video embedded images
538 if ( $active['av'] ) {
539 $exts = implode( '|', self::getAudioVideoExts() );
540 $thumbers[ $exts ] = array( __CLASS__, 'getAudioVideoThumbnail' );
541 }
542
543 // Ghostscript
544 if ( $active['gs'] && self::isGhostscriptAvailable() ) {
545 $exts = implode( '|', self::getGhostscriptExts() );
546 $thumbers[ $exts ] = array( __CLASS__, 'getGhostscriptThumbnail' );
547 }
548
549 // Imagick
550 if ( $active['imagick'] && self::isImagickAvailable() ) {
551 include_once DG_PATH . 'inc/class-image-editor-imagick.php';
552 if ( $exts = DG_Image_Editor_Imagick::query_formats() ) {
553 $exts = implode( '|', $exts );
554 $thumbers[ $exts ] = array( __CLASS__, 'getImagickThumbnail' );
555 }
556 }
557
558 // allow users to filter thumbers used
559 $thumbers = apply_filters( 'dg_thumbers', $thumbers );
560
561 // strip out anything that can't be called
562 $thumbers = array_filter( $thumbers, 'is_callable' );
563
564 // log which thumbers are being used
565 if ( DG_Logger::logEnabled() ) {
566 if ( count( $thumbers ) > 0 ) {
567 $entry = __( 'Thumbnail Generators: ', 'document-gallery' );
568 foreach ( $thumbers as $k => $v ) {
569 $thumber = DG_Util::callableToString($v);
570
571 // TODO: The following works for all internal regexes, but may have unpredictable
572 // results if developer adds additional thumbnail generators using different regexes
573 $filetypes = str_replace( '|', ', ', $k );
574
575 $entry .= PHP_EOL . "$thumber: $filetypes";
576 }
577 } else {
578 $entry = __( 'No thumbnail generators enabled.', 'document-gallery' );
579 }
580 DG_Logger::writeLog( DG_LogLevel::Detail, $entry );
581 }
582 }
583
584 return $thumbers;
585 }
586
587 /**
588 * Template that handles generating a thumbnail.
589 *
590 * If image has already been generated through other means, $pg may be set to the system path where the
591 * thumbnail is located. In this case, $generator will not be invoked, but *will* be kept for historical purposes.
592 *
593 * @param callable $generator Takes ID and pg and returns path to temp file or false.
594 * @param int $ID ID for the attachment that we need a thumbnail for.
595 * @param int|string $pg Page number of the attachment to get a thumbnail for or the system path to the image to be used.
596 *
597 * @return bool Whether generation was successful.
598 */
599 private static function thumbnailGenerationHarness( $generator, $ID, $pg = 1 ) {
600 // handle system page in $pg variable
601 if ( is_string( $pg ) && ! is_numeric( $pg ) ) {
602 $temp_path = $pg;
603 } // delegate thumbnail generation to $generator
604 elseif ( false === ( $temp_path = call_user_func( $generator, $ID, $pg ) ) ) {
605 return false;
606 }
607
608 // get some useful stuff
609 $doc_path = get_attached_file( $ID );
610 $doc_url = wp_get_attachment_url( $ID );
611 $dirname = dirname( $doc_path );
612 $basename = basename( $doc_path );
613 if ( false === ( $len = strrpos( $basename, '.' ) ) ) {
614 $len = strlen( $basename );
615 }
616 $extless = substr( $basename, 0, $len );
617 $ext = self::getExt( $temp_path );
618
619 $thumb_name = self::getUniqueThumbName( $dirname, $extless, $ext );
620 $thumb_path = $dirname . DIRECTORY_SEPARATOR . $thumb_name;
621
622 // scale generated image down
623 $img = wp_get_image_editor( $temp_path );
624
625 if ( is_wp_error( $img ) ) {
626 DG_Logger::writeLog(
627 DG_LogLevel::Error,
628 __( 'Failed to get image editor: ', 'document-gallery' ) . $img->get_error_message() );
629
630 return false;
631 }
632
633 $options = self::getOptions();
634 $img->resize( $options['width'], $options['height'], false );
635 $err = $img->save( $thumb_path );
636
637 if ( is_wp_error( $err ) ) {
638 DG_Logger::writeLog(
639 DG_LogLevel::Error,
640 __( 'Failed to save image: ', 'document-gallery' ) .
641 $err->get_error_message() );
642
643 return false;
644 }
645
646 // do some cleanup
647 @unlink( $temp_path );
648 self::deleteThumbMeta( $ID );
649
650 // store new thumbnail in DG options
651 $options['thumbs'][ $ID ] = array(
652 'timestamp' => time(),
653 'thumb_url' => preg_replace( '#' . preg_quote( $basename ) . '$#', $thumb_name, $doc_url ),
654 'thumb_path' => $thumb_path,
655 'thumber' => $generator
656 );
657 self::setOptions( $options );
658
659 return true;
660 }
661
662 /**
663 * Caller should handle removal of the temp file when finished.
664 *
665 * @param string $ext The extension to be given to the temp file.
666 *
667 * @return string A temp file with the given extension.
668 */
669 private static function getTempFile( $ext = 'png' ) {
670 static $base = null;
671 static $tmp;
672
673 if ( is_null( $base ) ) {
674 $base = md5( time() );
675 $tmp = untrailingslashit( get_temp_dir() );
676 }
677
678 return $tmp . DIRECTORY_SEPARATOR . wp_unique_filename( $tmp, $base . '.' . $ext );
679 }
680
681 /**
682 * Constructs name for file's thumbnail, ensuring that it does not conflict
683 * with any existing file.
684 *
685 * @param string $dirname Directory where the document is located.
686 * @param string $extless Base name, less the extension.
687 * @param string $ext The extension of the image to be created.
688 *
689 * @return string Name unique within the directory given, derived from the basename given.
690 */
691 private static function getUniqueThumbName( $dirname, $extless, $ext = 'png' ) {
692 return wp_unique_filename( $dirname, str_replace( '.', '-', $extless ) . '-thumb.' . $ext );
693 }
694
695 /**
696 * Removes the existing thumbnail/document meta for the attachment(s)
697 * with the ID(s), if such a thumbnails exists.
698 *
699 * @param int|array $ids
700 *
701 * @return array All IDs that were deleted -- some subset of IDs requested to be deleted.
702 */
703 public static function deleteThumbMeta( $ids ) {
704 $options = self::getOptions();
705
706 $deleted = array();
707 foreach ( (array) $ids as $id ) {
708 if ( isset( $options['thumbs'][ $id ] ) ) {
709 if ( isset( $options['thumbs'][ $id ]['thumber'] ) ) {
710 @unlink( $options['thumbs'][ $id ]['thumb_path'] );
711 }
712
713 unset( $options['thumbs'][ $id ] );
714 $deleted[] = $id;
715 }
716 }
717
718 if ( count( $deleted ) > 0 ) {
719 self::setOptions( $options );
720 }
721
722 return $deleted;
723 }
724
725 /**
726 * Checks whether exec() may be used.
727 * Source: http://stackoverflow.com/a/12980534/866618
728 *
729 * @return bool Whether exec() is available.
730 */
731 public static function isExecAvailable() {
732 static $available = null;
733
734 if ( is_null( $available ) ) {
735 $available = true;
736
737 if ( ini_get( 'safe_mode' ) ) {
738 $available = false;
739 } else {
740 $d = ini_get( 'disable_functions' );
741 $s = ini_get( 'suhosin.executor.func.blacklist' );
742 if ( "$d$s" ) {
743 $array = preg_split( '/,\s*/', "$d,$s" );
744 $available = ! in_array( 'exec', $array );
745 }
746 }
747 }
748
749 return $available;
750 }
751
752 /**
753 * Formerly achieved with wp_check_filetype(), but it was only returning
754 * valid results if the active user had permission to upload the given filetype.
755 *
756 * @param string $filename Name of the file to get extension from.
757 *
758 * @return bool|string Returns the file extension on success, false on failure.
759 */
760 private static function getExt( $filename ) {
761 if ( $ext = pathinfo( $filename, PATHINFO_EXTENSION ) ) {
762 $res = preg_grep( '/^(?:.*\|)?' . $ext . '(?:\|.*)?$/i', self::getAllExts() );
763 $res = reset( $res );
764 if ( $res === false ) {
765 $ext = false;
766 }
767 }
768
769 if ( ! $ext && ( $info = getimagesize( $filename ) ) && ( $ext = image_type_to_extension( $info[2], false ) ) ) {
770 return $ext;
771 }
772
773 return $ext;
774 }
775
776 /**
777 * Addresses issues with getting a complete list of supported MIME types as
778 * described in this issue: https://core.trac.wordpress.org/ticket/32544
779 * @return array Contains all MIME types supported by WordPress, including custom types added by plugins.
780 */
781 private static function getAllExts() {
782 return array_keys( array_merge( wp_get_mime_types(), get_allowed_mime_types() ) );
783 }
784
785 /**
786 * Blocks instantiation. All functions are static.
787 */
788 private function __construct() {
789
790 }
791 }