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

772 lines 26.6 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 DG_WPADMIN_PATH . '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 DG_WPINC_PATH . 'class-wp-image-editor.php';
192 include_once DG_WPINC_PATH . '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 * @return bool Whether we can use the GS executable.
299 */
300 public static function isGhostscriptAvailable() {
301 static $ret = null;
302
303 if (is_null($ret)) {
304 $options = self::getOptions();
305 $ret = $options['gs'] && self::isExecAvailable();
306 }
307
308 return $ret;
309 }
310
311 /*==========================================================================
312 * GOOGLE DRIVE VIEWER THUMBNAILS
313 *=========================================================================*/
314
315 /**
316 * Get thumbnail for document with given ID from Google Drive Viewer.
317 *
318 * NOTE: Caller must verify that extension is supported.
319 *
320 * @param str $ID The attachment ID to retrieve thumbnail for.
321 * @param int $pg The page number to make thumbnail of -- index starts at 1.
322 * @return bool|str False on failure, URL to thumb on success.
323 */
324 public static function getGoogleDriveThumbnail($ID_URL, $pg = 1) {
325 // User agent for Lynx 2.8.7rel.2 -- Why? Because I can.
326 static $user_agent = 'Lynx/2.8.7rel.2 libwww-FM/2.14 SSL-MM/1.4.1 OpenSSL/1.0.0a';
327 static $timeout = 60;
328
329 $google_viewer = 'https://docs.google.com/viewer?url=%s&a=bi&pagenumber=%d&w=%d';
330 $doc_url = wp_get_attachment_url($ID_URL);
331 if (!$doc_url) {
332 return false;
333 }
334
335 $temp_file = self::getTempFile();
336
337 // args for use in HTTP request
338 $args = array(
339 'timeout' => $timeout, // these requests can take a LONG time
340 'redirection' => 5,
341 'httpversion' => '1.0',
342 'user-agent' => $user_agent,
343 'blocking' => true,
344 'headers' => array(),
345 'cookies' => array(),
346 'body' => null,
347 'compress' => false,
348 'decompress' => true,
349 'sslverify' => true,
350 'stream' => true,
351 'filename' => $temp_file
352 );
353
354 // prevent PHP timeout before HTTP completes
355 set_time_limit($timeout);
356
357 $options = self::getOptions();
358 $google_viewer = sprintf($google_viewer, urlencode($doc_url), (int)$pg, $options['width']);
359
360 // get thumbnail from Google Drive Viewer & check for error on return
361 $response = wp_remote_get($google_viewer, $args);
362
363 if (is_wp_error($response) || !preg_match('/[23][0-9]{2}/', $response['response']['code'])) {
364 DocumentGallery::writeLog(__('Failed to retrieve thumbnail from Google: ', 'document-gallery') .
365 (is_wp_error($response)
366 ? $response->get_error_message()
367 : $response['response']['message']));
368
369 @unlink($temp_file);
370 return false;
371 }
372
373 return $temp_file;
374 }
375
376 /**
377 * @return array All extensions supported by Google Drive Viewer.
378 */
379 private static function getGoogleDriveExts() {
380 return array(
381 'tiff', 'bmp', 'doc', 'docx', 'xls', 'xlsx', 'ppt', 'pptx',
382 'pdf', 'pages', 'ai', 'psd', 'dxf', 'svg', 'eps', 'ps', 'ttf'
383 );
384 }
385
386 /**
387 * TODO: Currently always returns true.
388 * @return bool Whether Google Drive can access files on this system.
389 */
390 public static function isGoogleDriveAvailable() {
391 return true;
392 }
393
394 /*==========================================================================
395 * DEFAULT THUMBNAILS
396 *=========================================================================*/
397
398 /**
399 * Get thumbnail for document with given ID from default images.
400 *
401 * @param str $ID The attachment ID to retrieve thumbnail from.
402 * @param int $pg Unused.
403 * @return str URL to thumbnail.
404 */
405 public static function getDefaultThumbnail($ID, $pg = 1) {
406 $options = self::getOptions();
407 $width = $options['width'];
408 $height = $options['height'];
409 $icon_url = DG_URL . 'assets/icons/';
410
411 $url = wp_get_attachment_url($ID);
412 $ext = self::getExt($url);
413
414 // handle images
415 if ($icon = image_downsize($ID, array($width, $height))) {
416 $icon = $icon[0];
417 }
418 // default extension icon
419 elseif ($name = self::getDefaultIcon($ext)) {
420 $icon = $icon_url . $name;
421 }
422 // fallback to standard WP icons
423 elseif (!$icon = wp_mime_type_icon($ID)) {
424 // everything failed. This is bad...
425 $icon = $icon_url . 'missing.png';
426 }
427
428 return $icon;
429 }
430
431 /**
432 * Returns the name of the image to represent the filetype given.
433 *
434 * @param str $ext
435 * @return str
436 */
437 private static function getDefaultIcon($ext) {
438 // Maps file ext to default image name.
439 static $exts = array(
440 // Most Common First
441 'pdf' => 'pdf.png',
442
443 // MS Office
444 'doc|docx|docm|dotx|dotm' => 'msdoc.png',
445 'ppt|pot|pps|pptx|pptm|ppsx|ppsm|potx|potm|ppam|sldx|sldm' => 'msppt.png',
446 'xla|xls|xlt|xlw|xlsx|xlsm|xlsb|xltx|xltm|xlam' => 'msxls.png',
447 'mdb' => 'msaccess.png',
448
449 // iWork
450 'key' => 'key.png',
451 'numbers' => 'numbers.png',
452 'pages' => 'pages.png',
453
454 // Images
455 'jpg|jpeg|jpe|gif|png|bmp|tif|tiff|ico' => 'image.png',
456
457 // Video formats
458 'asf|asx|wmv|wmx|wm|avi|divx|flv|mov' => 'video.png',
459 'qt|mpeg|mpg|mpe|mp4|m4v|ogv|webm|mkv' => 'video.png',
460
461 // Audio formats
462 'mp3|m4a|m4b|ra|ram|wav|ogg|oga|wma|wax|mka' => 'audio.png',
463 'midi|mid' => 'midi.png',
464
465 // Text formats
466 'txt|tsv|csv' => 'text.png',
467 'rtx' => 'rtx.png',
468 'rtf' => 'rtf.png',
469 'ics' => 'ics.png',
470 'wp|wpd' => 'wordperfect.png',
471
472 // Programming
473 'html|htm' => 'html.png',
474 'css' => 'css.png',
475 'js' => 'javascript.png',
476 'class' => 'java.png',
477 'asc' => 'asc.png',
478 'c' => 'c.png',
479 'cc|cpp' => 'cpp.png',
480 'h' => 'h.png',
481
482 // Msc application formats
483 'zip|tar|gzip|gz|bz2|tgz|7z|rar' => 'compressed.png',
484 'exe' => 'exec.png',
485 'swf' => 'shockwave.png',
486
487 // OpenDocument formats
488 'odt' => 'opendocument-text.png',
489 'odp' => 'opendocument-presentation.png',
490 'ods' => 'opendocument-spreadsheet.png',
491 'odg' => 'opendocument-graphics.png',
492 'odb' => 'opendocument-database.png',
493 'odf' => 'opendocument-formula.png'
494 );
495
496 foreach ($exts as $ext_preg => $icon) {
497 $ext_preg = '!(' . $ext_preg . ')$!i';
498 if (preg_match($ext_preg, $ext)) {
499 return $icon;
500 }
501 }
502
503 return false;
504 }
505
506 /*==========================================================================
507 * GENERAL THUMBNAIL HELPER FUNCTIONS
508 *=========================================================================*/
509
510 /**
511 * @return array WP_Post objects for each attachment that has been processed.
512 */
513 public static function getThumbed() {
514 $options = self::getOptions();
515 $args = array(
516 'post_type' => 'attachment',
517 'post_status' => 'inherit',
518 'post_per_page' => -1,
519 'post__in' => array_keys($options['thumbs'])
520 );
521
522 return count($args['post__in']) ? get_posts($args) : array();
523 }
524
525 /**
526 * Key: Attachment ID
527 * Val: array
528 * + created_timestamp - When the thumbnail was generated.
529 * + thumb_path - System path to thumbnail image.
530 * + thumb_url - URL pointing to the thumbnail for this document.
531 * + thumber - Generator used to create thumb OR false if failed to gen.
532 * @return array|null Thumber options from DB or null if options not initialized.
533 */
534 public static function getOptions() {
535 global $dg_options;
536 return !empty($dg_options) && isset($dg_options['thumber'])
537 ? $dg_options['thumber']
538 : null;
539 }
540
541 /**
542 * Key: Attachment ID
543 * Val: array
544 * + created_timestamp - When the thumbnail was generated.
545 * + thumb_path - System path to thumbnail image.
546 * + thumb_url - URL pointing to the thumbnail for this document.
547 * + thumber - Generator used to create thumb OR false if failed to gen.
548 * @param array $options Thumber options to store in DB
549 */
550 private static function setOptions($options) {
551 global $dg_options;
552 $dg_options['thumber'] = $options;
553 update_option(DG_OPTION_NAME, $dg_options);
554 }
555
556 /**
557 * @filter dg_thumbers Allows developers to filter the Thumbers used
558 * for specific filetypes. Index is the regex to match file extensions
559 * supported and the value is anything that can be accepted by call_user_func().
560 * The function must take two parameters, 1st is the int ID of the attachment
561 * to get a thumbnail for, 2nd is the page to take a thumbnail of
562 * (may not be relevant for some filetypes).
563 *
564 * @return array
565 */
566 private static function getThumbers() {
567 static $thumbers = false;
568
569 if (false === $thumbers) {
570 $options = self::getOptions();
571 $active = $options['active'];
572 $thumbers = array();
573
574 // Audio/Video embedded images
575 if ($active['av']) {
576 $exts = implode('|', self::getAudioVideoExts());
577 $thumbers[$exts] = array(__CLASS__, 'getAudioVideoThumbnail');
578 }
579
580 // Ghostscript
581 if ($active['gs'] && false !== self::getGhostscriptExecutable()) {
582 $exts = implode('|', self::getGhostscriptExts());
583 $thumbers[$exts] = array(__CLASS__, 'getGhostscriptThumbnail');
584 }
585
586 // Imagick
587 if ($active['imagick'] && self::isImagickAvailable()) {
588 include_once DG_PATH . 'inc/class-image-editor-imagick.php';
589 if ($exts = DG_Image_Editor_Imagick::query_formats()) {
590 $exts = implode('|', $exts);
591 $thumbers[$exts] = array(__CLASS__, 'getImagickThumbnail');
592 }
593 }
594
595 // Google Drive Viewer
596 if ($active['google']) {
597 $exts = implode('|', self::getGoogleDriveExts());
598 $thumbers[$exts] = array(__CLASS__, 'getGoogleDriveThumbnail');
599 }
600
601 // allow users to filter thumbers used
602 $thumbers = apply_filters('dg_thumbers', $thumbers);
603
604 // strip out anything that can't be called
605 $thumbers = array_filter($thumbers, 'is_callable');
606 }
607
608 return $thumbers;
609 }
610
611 /**
612 * Template that handles generating a thumbnail.
613 *
614 * @param callable $generator Takes ID and pg and returns path to temp file or false.
615 * @param int $ID ID for the attachment that we need a thumbnail for.
616 * @param int $pg Page number of the attachment to get a thumbnail for.
617 * @return bool|array Array containing 'url' and 'path' values or false.
618 */
619 public static function getThumbnailTemplate($generator, $ID, $pg = 1) {
620 // delegate thumbnail generation to $generator
621 if (false === ($temp_path = call_user_func($generator, $ID, $pg))) {
622 return false;
623 }
624
625 // get some useful stuff
626 $doc_path = get_attached_file($ID);
627 $doc_url = wp_get_attachment_url($ID);
628 $dirname = dirname($doc_path);
629 $basename = basename($doc_path);
630 if (false === ($len = strrpos($basename, '.'))) {
631 $len = strlen($basename);
632 }
633 $extless = substr($basename, 0, $len);
634 $ext = self::getExt($temp_path);
635
636 $thumb_name = self::getUniqueThumbName($dirname, $extless, $ext);
637 $thumb_path = $dirname . DIRECTORY_SEPARATOR . $thumb_name;
638
639 // scale generated image down
640 $img = wp_get_image_editor($temp_path);
641
642 if (is_wp_error($img)) {
643 DocumentGallery::writeLog(
644 __('Failed to get image editor: ', 'document-gallery') .
645 $img->get_error_message());
646 return false;
647 }
648
649 $options = self::getOptions();
650 $img->resize($options['width'], $options['height'], false);
651 $err = $img->save($thumb_path);
652
653 if (is_wp_error($err)) {
654 DocumentGallery::writeLog(
655 __('Failed to save image: ', 'document-gallery') .
656 $err->get_error_message());
657 return false;
658 }
659
660 // do some cleanup
661 @unlink($temp_path);
662 self::deleteThumbMeta($ID);
663
664 return array(
665 'path' => $thumb_path,
666 'url' => preg_replace('#'.preg_quote($basename).'$#', $thumb_name, $doc_url));
667 }
668
669 /**
670 * Caller should handle removal of the temp file when finished.
671 *
672 * @staticvar int $count
673 * @param str $ext
674 */
675 private static function getTempFile($ext = 'png') {
676 static $base = null;
677 static $tmp;
678
679 if (is_null($base)) {
680 $base = md5(time());
681 $tmp = untrailingslashit(get_temp_dir());
682 }
683
684 return $tmp . DIRECTORY_SEPARATOR . wp_unique_filename($tmp, "$base.$ext");
685 }
686
687 /**
688 * Constructs name for file's thumbnail, ensuring that it does not conflict
689 * with any existing file.
690 *
691 * @param str $dirname Directory where the document is located.
692 * @param str $extless Base name, less the extension.
693 * @param str $ext The extension of the image to be created.
694 * @return str Name unique within the directory given, derived from the basename given.
695 */
696 private static function getUniqueThumbName($dirname, $extless, $ext = 'png') {
697 return wp_unique_filename($dirname, str_replace('.', '-', $extless) . '-thumb.' . $ext);
698 }
699
700 /**
701 * Removes the existing thumbnail/document meta for the attachment
702 * with $ID, if such a thumbnail exists.
703 *
704 * @param int $ID
705 */
706 public static function deleteThumbMeta($ID) {
707 $options = self::getOptions();
708
709 if (isset($options['thumbs'][$ID])) {
710 if (false !== $options['thumbs'][$ID]) {
711 @unlink($options['thumbs'][$ID]['thumb_path']);
712 }
713
714 unset($options['thumbs'][$ID]);
715 self::setOptions($options);
716 }
717 }
718
719 /**
720 * Checks whether exec() may be used.
721 * Source: http://stackoverflow.com/a/12980534/866618
722 *
723 * @return bool Whether exec() is available.
724 */
725 public static function isExecAvailable() {
726 static $available = null;
727
728 if (is_null($available)) {
729 $available = true;
730
731 if (ini_get('safe_mode')) {
732 $available = false;
733 } else {
734 $d = ini_get('disable_functions');
735 $s = ini_get('suhosin.executor.func.blacklist');
736 if ("$d$s") {
737 $array = preg_split('/,\s*/', "$d,$s");
738 $available = !in_array('exec', $array);
739 }
740 }
741 }
742
743 return $available;
744 }
745
746 /**
747 * Formerly achieved with wp_check_filetype(), but it was only returning
748 * valid results if the active user had permission to upload the given filetype.
749 *
750 * @param str $filename Name of the file to get extension from.
751 * @return str|bool Returns the file extension on success, false on failure.
752 */
753 private static function getExt($filename) {
754 foreach (wp_get_mime_types() as $ext_preg => $unused) {
755 $ext_preg = '!\.(' . $ext_preg . ')$!i';
756 if (preg_match($ext_preg, $filename, $ext_matches)) {
757 return $ext_matches[1];
758 }
759 }
760
761 return false;
762 }
763
764 /**
765 * Blocks instantiation. All functions are static.
766 */
767 private function __construct() {
768
769 }
770 }
771
772 ?>