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

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