PluginProbe
Document Gallery / 2.0.4
Document Gallery v2.0.4
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 2.0.4, at inc/class-thumber.php

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