Page MenuHomestyx hydra

No OneTemporary

diff --git a/src/applications/files/transform/PhabricatorFileImageTransform.php b/src/applications/files/transform/PhabricatorFileImageTransform.php
index 468eae0e03..98ffcdd706 100644
--- a/src/applications/files/transform/PhabricatorFileImageTransform.php
+++ b/src/applications/files/transform/PhabricatorFileImageTransform.php
@@ -1,382 +1,386 @@
<?php
abstract class PhabricatorFileImageTransform extends PhabricatorFileTransform {
private $file;
private $data;
private $image;
private $imageX;
private $imageY;
/**
* Get an estimate of the transformed dimensions of a file.
*
* @param PhabricatorFile File to transform.
* @return list<int, int>|null Width and height, if available.
*/
public function getTransformedDimensions(PhabricatorFile $file) {
return null;
}
public function canApplyTransform(PhabricatorFile $file) {
if (!$file->isViewableImage()) {
return false;
}
if (!$file->isTransformableImage()) {
return false;
}
return true;
}
protected function willTransformFile(PhabricatorFile $file) {
$this->file = $file;
$this->data = null;
$this->image = null;
$this->imageX = null;
$this->imageY = null;
}
protected function getFileProperties() {
return array();
}
protected function applyCropAndScale(
$dst_w, $dst_h,
$src_x, $src_y,
$src_w, $src_h,
$use_w, $use_h,
$scale_up) {
// Figure out the effective destination width, height, and offsets.
$cpy_w = min($dst_w, $use_w);
$cpy_h = min($dst_h, $use_h);
// If we aren't scaling up, and are copying a very small source image,
// we're just going to center it in the destination image.
if (!$scale_up) {
$cpy_w = min($cpy_w, $src_w);
$cpy_h = min($cpy_h, $src_h);
}
$off_x = ($dst_w - $cpy_w) / 2;
$off_y = ($dst_h - $cpy_h) / 2;
if ($this->shouldUseImagemagick()) {
$argv = array();
$argv[] = '-coalesce';
$argv[] = '-shave';
$argv[] = $src_x.'x'.$src_y;
$argv[] = '-resize';
if ($scale_up) {
$argv[] = $dst_w.'x'.$dst_h;
} else {
$argv[] = $dst_w.'x'.$dst_h.'>';
}
$argv[] = '-bordercolor';
$argv[] = 'rgba(255, 255, 255, 0)';
$argv[] = '-border';
$argv[] = $off_x.'x'.$off_y;
return $this->applyImagemagick($argv);
}
$src = $this->getImage();
$dst = $this->newEmptyImage($dst_w, $dst_h);
$trap = new PhutilErrorTrap();
$ok = @imagecopyresampled(
$dst,
$src,
$off_x, $off_y,
$src_x, $src_y,
$cpy_w, $cpy_h,
$src_w, $src_h);
$errors = $trap->getErrorsAsString();
$trap->destroy();
if ($ok === false) {
throw new Exception(
pht(
'Failed to imagecopyresampled() image: %s',
$errors));
}
$data = PhabricatorImageTransformer::saveImageDataInAnyFormat(
$dst,
$this->file->getMimeType());
return $this->newFileFromData($data);
}
protected function applyImagemagick(array $argv) {
$tmp = new TempFile();
Filesystem::writeFile($tmp, $this->getData());
$out = new TempFile();
$future = new ExecFuture('convert %s %Ls %s', $tmp, $argv, $out);
// Don't spend more than 60 seconds resizing; just fail if it takes longer
// than that.
$future->setTimeout(60)->resolvex();
$data = Filesystem::readFile($out);
return $this->newFileFromData($data);
}
/**
* Create a new @{class:PhabricatorFile} from raw data.
*
* @param string Raw file data.
*/
protected function newFileFromData($data) {
if ($this->file) {
$name = $this->file->getName();
+ $inherit_properties = array(
+ 'viewPolicy' => $this->file->getViewPolicy(),
+ );
} else {
$name = 'default.png';
+ $inherit_properties = array();
}
$defaults = array(
'canCDN' => true,
'name' => $this->getTransformKey().'-'.$name,
);
- $properties = $this->getFileProperties() + $defaults;
+ $properties = $this->getFileProperties() + $inherit_properties + $defaults;
return PhabricatorFile::newFromFileData($data, $properties);
}
/**
* Create a new image filled with transparent pixels.
*
* @param int Desired image width.
* @param int Desired image height.
* @return resource New image resource.
*/
protected function newEmptyImage($w, $h) {
$w = (int)$w;
$h = (int)$h;
if (($w <= 0) || ($h <= 0)) {
throw new Exception(
pht('Can not create an image with nonpositive dimensions.'));
}
$trap = new PhutilErrorTrap();
$img = @imagecreatetruecolor($w, $h);
$errors = $trap->getErrorsAsString();
$trap->destroy();
if ($img === false) {
throw new Exception(
pht(
'Unable to imagecreatetruecolor() a new empty image: %s',
$errors));
}
$trap = new PhutilErrorTrap();
$ok = @imagesavealpha($img, true);
$errors = $trap->getErrorsAsString();
$trap->destroy();
if ($ok === false) {
throw new Exception(
pht(
'Unable to imagesavealpha() a new empty image: %s',
$errors));
}
$trap = new PhutilErrorTrap();
$color = @imagecolorallocatealpha($img, 255, 255, 255, 127);
$errors = $trap->getErrorsAsString();
$trap->destroy();
if ($color === false) {
throw new Exception(
pht(
'Unable to imagecolorallocatealpha() a new empty image: %s',
$errors));
}
$trap = new PhutilErrorTrap();
$ok = @imagefill($img, 0, 0, $color);
$errors = $trap->getErrorsAsString();
$trap->destroy();
if ($ok === false) {
throw new Exception(
pht(
'Unable to imagefill() a new empty image: %s',
$errors));
}
return $img;
}
/**
* Get the pixel dimensions of the image being transformed.
*
* @return list<int, int> Width and height of the image.
*/
protected function getImageDimensions() {
if ($this->imageX === null) {
$image = $this->getImage();
$trap = new PhutilErrorTrap();
$x = @imagesx($image);
$y = @imagesy($image);
$errors = $trap->getErrorsAsString();
$trap->destroy();
if (($x === false) || ($y === false) || ($x <= 0) || ($y <= 0)) {
throw new Exception(
pht(
'Unable to determine image dimensions with '.
'imagesx()/imagesy(): %s',
$errors));
}
$this->imageX = $x;
$this->imageY = $y;
}
return array($this->imageX, $this->imageY);
}
/**
* Get the raw file data for the image being transformed.
*
* @return string Raw file data.
*/
protected function getData() {
if ($this->data !== null) {
return $this->data;
}
$file = $this->file;
$max_size = (1024 * 1024 * 16);
$img_size = $file->getByteSize();
if ($img_size > $max_size) {
throw new Exception(
pht(
'This image is too large to transform. The transform limit is %s '.
'bytes, but the image size is %s bytes.',
new PhutilNumber($max_size),
new PhutilNumber($img_size)));
}
$data = $file->loadFileData();
$this->data = $data;
return $this->data;
}
/**
* Get the GD image resource for the image being transformed.
*
* @return resource GD image resource.
*/
protected function getImage() {
if ($this->image !== null) {
return $this->image;
}
if (!function_exists('imagecreatefromstring')) {
throw new Exception(
pht(
'Unable to transform image: the imagecreatefromstring() function '.
'is not available. Install or enable the "gd" extension for PHP.'));
}
$data = $this->getData();
$data = (string)$data;
// First, we're going to write the file to disk and use getimagesize()
// to determine its dimensions without actually loading the pixel data
// into memory. For very large images, we'll bail out.
// In particular, this defuses a resource exhaustion attack where the
// attacker uploads a 40,000 x 40,000 pixel PNGs of solid white. These
// kinds of files compress extremely well, but require a huge amount
// of memory and CPU to process.
$tmp = new TempFile();
Filesystem::writeFile($tmp, $data);
$tmp_path = (string)$tmp;
$trap = new PhutilErrorTrap();
$info = @getimagesize($tmp_path);
$errors = $trap->getErrorsAsString();
$trap->destroy();
unset($tmp);
if ($info === false) {
throw new Exception(
pht(
'Unable to get image information with getimagesize(): %s',
$errors));
}
list($width, $height) = $info;
if (($width <= 0) || ($height <= 0)) {
throw new Exception(
pht(
'Unable to determine image width and height with getimagesize().'));
}
$max_pixels = (4096 * 4096);
$img_pixels = ($width * $height);
if ($img_pixels > $max_pixels) {
throw new Exception(
pht(
'This image (with dimensions %spx x %spx) is too large to '.
'transform. The image has %s pixels, but transforms are limited '.
'to images with %s or fewer pixels.',
new PhutilNumber($width),
new PhutilNumber($height),
new PhutilNumber($img_pixels),
new PhutilNumber($max_pixels)));
}
$trap = new PhutilErrorTrap();
$image = @imagecreatefromstring($data);
$errors = $trap->getErrorsAsString();
$trap->destroy();
if ($image === false) {
throw new Exception(
pht(
'Unable to load image data with imagecreatefromstring(): %s',
$errors));
}
$this->image = $image;
return $this->image;
}
private function shouldUseImagemagick() {
if (!PhabricatorEnv::getEnvConfig('files.enable-imagemagick')) {
return false;
}
if ($this->file->getMimeType() != 'image/gif') {
return false;
}
// Don't try to preserve the animation in huge GIFs.
list($x, $y) = $this->getImageDimensions();
if (($x * $y) > (512 * 512)) {
return false;
}
return true;
}
}
diff --git a/src/applications/files/transform/PhabricatorFileThumbnailTransform.php b/src/applications/files/transform/PhabricatorFileThumbnailTransform.php
index 2c61743078..6cf3914556 100644
--- a/src/applications/files/transform/PhabricatorFileThumbnailTransform.php
+++ b/src/applications/files/transform/PhabricatorFileThumbnailTransform.php
@@ -1,231 +1,231 @@
<?php
final class PhabricatorFileThumbnailTransform
extends PhabricatorFileImageTransform {
const TRANSFORM_PROFILE = 'profile';
const TRANSFORM_PINBOARD = 'pinboard';
const TRANSFORM_THUMBGRID = 'thumbgrid';
const TRANSFORM_PREVIEW = 'preview';
const TRANSFORM_WORKCARD = 'workcard';
private $name;
private $key;
private $dstX;
private $dstY;
private $scaleUp;
public function setName($name) {
$this->name = $name;
return $this;
}
public function setKey($key) {
$this->key = $key;
return $this;
}
public function setDimensions($x, $y) {
$this->dstX = $x;
$this->dstY = $y;
return $this;
}
public function setScaleUp($scale) {
$this->scaleUp = $scale;
return $this;
}
public function getTransformName() {
return $this->name;
}
public function getTransformKey() {
return $this->key;
}
protected function getFileProperties() {
$properties = array();
switch ($this->key) {
case self::TRANSFORM_PROFILE:
$properties['profile'] = true;
$properties['name'] = 'profile';
break;
}
return $properties;
}
public function generateTransforms() {
return array(
id(new PhabricatorFileThumbnailTransform())
- ->setName(pht("Profile (400px \xC3\x97 400px)"))
+ ->setName(pht("Profile (400px \xC3\x97 400px) (Image will be Public)"))
->setKey(self::TRANSFORM_PROFILE)
->setDimensions(400, 400)
->setScaleUp(true),
id(new PhabricatorFileThumbnailTransform())
->setName(pht("Pinboard (280px \xC3\x97 210px)"))
->setKey(self::TRANSFORM_PINBOARD)
->setDimensions(280, 210),
id(new PhabricatorFileThumbnailTransform())
->setName(pht('Thumbgrid (100px)'))
->setKey(self::TRANSFORM_THUMBGRID)
->setDimensions(100, null),
id(new PhabricatorFileThumbnailTransform())
->setName(pht('Preview (220px)'))
->setKey(self::TRANSFORM_PREVIEW)
->setDimensions(220, null),
id(new self())
->setName(pht('Workcard (526px)'))
->setKey(self::TRANSFORM_WORKCARD)
->setScaleUp(true)
->setDimensions(526, null),
);
}
public function applyTransform(PhabricatorFile $file) {
$this->willTransformFile($file);
list($src_x, $src_y) = $this->getImageDimensions();
$dst_x = $this->dstX;
$dst_y = $this->dstY;
$dimensions = $this->computeDimensions(
$src_x,
$src_y,
$dst_x,
$dst_y);
$copy_x = $dimensions['copy_x'];
$copy_y = $dimensions['copy_y'];
$use_x = $dimensions['use_x'];
$use_y = $dimensions['use_y'];
$dst_x = $dimensions['dst_x'];
$dst_y = $dimensions['dst_y'];
return $this->applyCropAndScale(
$dst_x,
$dst_y,
($src_x - $copy_x) / 2,
($src_y - $copy_y) / 2,
$copy_x,
$copy_y,
$use_x,
$use_y,
$this->scaleUp);
}
public function getTransformedDimensions(PhabricatorFile $file) {
$dst_x = $this->dstX;
$dst_y = $this->dstY;
// If this is transform has fixed dimensions, we can trivially predict
// the dimensions of the transformed file.
if ($dst_y !== null) {
return array($dst_x, $dst_y);
}
$src_x = $file->getImageWidth();
$src_y = $file->getImageHeight();
if (!$src_x || !$src_y) {
return null;
}
$dimensions = $this->computeDimensions(
$src_x,
$src_y,
$dst_x,
$dst_y);
return array($dimensions['dst_x'], $dimensions['dst_y']);
}
private function computeDimensions($src_x, $src_y, $dst_x, $dst_y) {
if ($dst_y === null) {
// If we only have one dimension, it represents a maximum dimension.
// The other dimension of the transform is scaled appropriately, except
// that we never generate images with crazily extreme aspect ratios.
if ($src_x < $src_y) {
// This is a tall, narrow image. Use the maximum dimension for the
// height and scale the width.
$use_y = $dst_x;
$dst_y = $dst_x;
$use_x = $dst_y * ($src_x / $src_y);
$dst_x = max($dst_y / 4, $use_x);
} else {
// This is a short, wide image. Use the maximum dimension for the width
// and scale the height.
$use_x = $dst_x;
$use_y = $dst_x * ($src_y / $src_x);
$dst_y = max($dst_x / 4, $use_y);
}
// In this mode, we always copy the entire source image. We may generate
// margins in the output.
$copy_x = $src_x;
$copy_y = $src_y;
} else {
$scale_up = $this->scaleUp;
// Otherwise, both dimensions are fixed. Figure out how much we'd have to
// scale the image down along each dimension to get the entire thing to
// fit.
$scale_x = ($dst_x / $src_x);
$scale_y = ($dst_y / $src_y);
if (!$scale_up) {
$scale_x = min($scale_x, 1);
$scale_y = min($scale_y, 1);
}
if ($scale_x > $scale_y) {
// This image is relatively tall and narrow. We're going to crop off the
// top and bottom.
$scale = $scale_x;
} else {
// This image is relatively short and wide. We're going to crop off the
// left and right.
$scale = $scale_y;
}
$copy_x = $dst_x / $scale;
$copy_y = $dst_y / $scale;
if (!$scale_up) {
$copy_x = min($src_x, $copy_x);
$copy_y = min($src_y, $copy_y);
}
// In this mode, we always use the entire destination image. We may
// crop the source input.
$use_x = $dst_x;
$use_y = $dst_y;
}
return array(
'copy_x' => $copy_x,
'copy_y' => $copy_y,
'use_x' => $use_x,
'use_y' => $use_y,
'dst_x' => $dst_x,
'dst_y' => $dst_y,
);
}
public function getDefaultTransform(PhabricatorFile $file) {
$x = (int)$this->dstX;
$y = (int)$this->dstY;
$name = 'image-'.$x.'x'.nonempty($y, $x).'.png';
$root = dirname(phutil_get_library_root('phabricator'));
$data = Filesystem::readFile($root.'/resources/builtin/'.$name);
return $this->newFileFromData($data);
}
}

File Metadata

Mime Type
text/x-diff
Expires
Tue, Apr 29, 4:15 PM (22 h, 42 m)
Storage Engine
blob
Storage Format
Raw Data
Storage Handle
108441
Default Alt Text
(16 KB)

Event Timeline