diff --git a/src/applications/files/controller/transform/PhabricatorFileTransformController.php b/src/applications/files/controller/transform/PhabricatorFileTransformController.php index c766bbbaca..7f3a42253d 100644 --- a/src/applications/files/controller/transform/PhabricatorFileTransformController.php +++ b/src/applications/files/controller/transform/PhabricatorFileTransformController.php @@ -43,12 +43,10 @@ class PhabricatorFileTransformController extends PhabricatorFileController { return new Aphront404Response(); } - if (!$file->isViewableInBrowser()) { - return new Aphront400Response(); - } + $type = $file->getMimeType(); - if (!$file->isTransformableImage()) { - return new Aphront400Response(); + if (!$file->isViewableInBrowser() || !$file->isTransformableImage()) { + return $this->buildDefaultTransformation($file); } switch ($this->transform) { @@ -78,6 +76,39 @@ class PhabricatorFileTransformController extends PhabricatorFileController { return $this->buildTransformedFileResponse($xform); } + private function buildDefaultTransformation(PhabricatorFile $file) { + static $regexps = array( + '@application/zip@' => 'zip', + '@image/@' => 'image', + '@application/pdf@' => 'pdf', + '@.*@' => 'default', + ); + + $type = $file->getMimeType(); + $prefix = 'default'; + foreach ($regexps as $regexp => $implied_prefix) { + if (preg_match($regexp, $type)) { + $prefix = $implied_prefix; + break; + } + } + + switch ($this->transform) { + case 'thumb-160x120': + $suffix = '160x120'; + break; + case 'thumb-60x45': + $suffix = '60x45'; + break; + default: + throw new Exception("Unsupported transformation type!"); + } + + $path = "/rsrc/image/icon/fatcow/thumbnails/{$prefix}{$suffix}.png"; + return id(new AphrontRedirectResponse()) + ->setURI($path); + } + private function buildTransformedFileResponse( PhabricatorTransformedFile $xform) { @@ -123,9 +154,25 @@ class PhabricatorFileTransformController extends PhabricatorFileController { $dx, $dy, $scale * $dx, $scale * $dy); - ob_start(); - imagejpeg($dst); - return ob_get_clean(); + $img = null; + + if (function_exists('imagejpeg')) { + ob_start(); + imagejpeg($dst); + $img = ob_get_clean(); + } else if (function_exists('imagepng')) { + ob_start(); + imagepng($dst); + $img = ob_get_clean(); + } else if (function_exists('imagegif')) { + ob_start(); + imagegif($dst); + $img = ob_get_clean(); + } else { + throw new Exception("No image generation functions exist!"); + } + + return $img; } } diff --git a/src/applications/files/storage/file/PhabricatorFile.php b/src/applications/files/storage/file/PhabricatorFile.php index b2a53948cd..0c70be9320 100644 --- a/src/applications/files/storage/file/PhabricatorFile.php +++ b/src/applications/files/storage/file/PhabricatorFile.php @@ -216,7 +216,35 @@ class PhabricatorFile extends PhabricatorFileDAO { } public function isTransformableImage() { - return preg_match('@^image/(gif|png|jpe?g)@', $this->getViewableMimeType()); + + // NOTE: The way the 'gd' extension works in PHP is that you can install it + // with support for only some file types, so it might be able to handle + // PNG but not JPEG. Try to generate thumbnails for whatever we can. Setup + // warns you if you don't have complete support. + + $matches = null; + $ok = preg_match( + '@^image/(gif|png|jpe?g)@', + $this->getViewableMimeType(), + $matches); + if (!$ok) { + return false; + } + + switch ($matches[1]) { + case 'jpg'; + case 'jpeg': + return function_exists('imagejpeg'); + break; + case 'png': + return function_exists('imagepng'); + break; + case 'gif': + return function_exists('imagegif'); + break; + default: + throw new Exception('Unknown type matched as image MIME type.'); + } } public function getViewableMimeType() { diff --git a/src/infrastructure/setup/PhabricatorSetup.php b/src/infrastructure/setup/PhabricatorSetup.php index 023de19800..ff4e559b79 100644 --- a/src/infrastructure/setup/PhabricatorSetup.php +++ b/src/infrastructure/setup/PhabricatorSetup.php @@ -165,6 +165,39 @@ class PhabricatorSetup { self::write("[OKAY] Basic configuration OKAY\n"); + + $issue_gd_warning = false; + self::writeHeader('GD LIBRARY'); + if (extension_loaded('gd')) { + self::write(" okay Extension 'gd' is loaded.\n"); + $image_type_map = array( + 'imagepng' => 'PNG', + 'imagegif' => 'GIF', + 'imagejpeg' => 'JPEG', + ); + foreach ($image_type_map as $function => $image_type) { + if (function_exists($function)) { + self::write(" okay Support for '{$image_type}' is available.\n"); + } else { + self::write(" warn Support for '{$image_type}' is not available!\n"); + $issue_gd_warning = true; + } + } + } else { + self::write(" warn Extension 'gd' is not loaded.\n"); + $issue_gd_warning = true; + } + + if ($issue_gd_warning) { + self::write( + "[WARN] The 'gd' library is missing or lacks full support. ". + "Phabricator will not be able to generate image thumbnails without ". + "gd.\n"); + } else { + self::write("[OKAY] 'gd' loaded and has full image type support.\n"); + } + + self::writeHeader('FACEBOOK INTEGRATION'); $fb_auth = PhabricatorEnv::getEnvConfig('facebook.auth-enabled'); if (!$fb_auth) { diff --git a/webroot/rsrc/image/icon/fatcow/thumbnails/default160x120.png b/webroot/rsrc/image/icon/fatcow/thumbnails/default160x120.png new file mode 100644 index 0000000000..16d6fd4f90 Binary files /dev/null and b/webroot/rsrc/image/icon/fatcow/thumbnails/default160x120.png differ diff --git a/webroot/rsrc/image/icon/fatcow/thumbnails/default60x45.png b/webroot/rsrc/image/icon/fatcow/thumbnails/default60x45.png new file mode 100644 index 0000000000..145ea1eb63 Binary files /dev/null and b/webroot/rsrc/image/icon/fatcow/thumbnails/default60x45.png differ diff --git a/webroot/rsrc/image/icon/fatcow/thumbnails/image160x120.png b/webroot/rsrc/image/icon/fatcow/thumbnails/image160x120.png new file mode 100644 index 0000000000..90cc11c02d Binary files /dev/null and b/webroot/rsrc/image/icon/fatcow/thumbnails/image160x120.png differ diff --git a/webroot/rsrc/image/icon/fatcow/thumbnails/image60x45.png b/webroot/rsrc/image/icon/fatcow/thumbnails/image60x45.png new file mode 100644 index 0000000000..9077e69586 Binary files /dev/null and b/webroot/rsrc/image/icon/fatcow/thumbnails/image60x45.png differ diff --git a/webroot/rsrc/image/icon/fatcow/thumbnails/pdf160x120.png b/webroot/rsrc/image/icon/fatcow/thumbnails/pdf160x120.png new file mode 100644 index 0000000000..20f08f955b Binary files /dev/null and b/webroot/rsrc/image/icon/fatcow/thumbnails/pdf160x120.png differ diff --git a/webroot/rsrc/image/icon/fatcow/thumbnails/pdf60x45.png b/webroot/rsrc/image/icon/fatcow/thumbnails/pdf60x45.png new file mode 100644 index 0000000000..8a16eaf488 Binary files /dev/null and b/webroot/rsrc/image/icon/fatcow/thumbnails/pdf60x45.png differ diff --git a/webroot/rsrc/image/icon/fatcow/thumbnails/zip160x120.png b/webroot/rsrc/image/icon/fatcow/thumbnails/zip160x120.png new file mode 100644 index 0000000000..fbe19e59f6 Binary files /dev/null and b/webroot/rsrc/image/icon/fatcow/thumbnails/zip160x120.png differ diff --git a/webroot/rsrc/image/icon/fatcow/thumbnails/zip60x45.png b/webroot/rsrc/image/icon/fatcow/thumbnails/zip60x45.png new file mode 100644 index 0000000000..c66416c318 Binary files /dev/null and b/webroot/rsrc/image/icon/fatcow/thumbnails/zip60x45.png differ