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

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