@recaptime-dev's working patches + fork for Phorge, a community fork of Phabricator. (Upstream dev and stable branches are at upstream/main and upstream/stable respectively.) hq.recaptime.dev/wiki/Phorge
phorge phabricator
1
fork

Configure Feed

Select the types of activity you want to include in your feed.

Convert inline profile image transforms to new transformations

Summary:
Ref T7707. Fixes T7879. Fixes T4406. When creating profile images:

- Use the new transforms;
- mark them as "profile" images so they're forced to the most-open policies.

Test Plan:
- Set restrictive default file policies.
- Changed profile picture, project pictures, etc. Verified they were visible to logged-out users.
- Registered via OAuth.
- Updated a Conpherence thread image.
- Browsed around looking for profile images, fixed sizing on everything I could find.

Reviewers: chad, btrahan

Reviewed By: btrahan

Subscribers: epriestley

Maniphest Tasks: T7879, T7707, T4406

Differential Revision: https://secure.phabricator.com/D12821

+67 -193
+3 -11
src/applications/auth/controller/PhabricatorAuthRegisterController.php
··· 604 604 return null; 605 605 } 606 606 607 - try { 608 - $xformer = new PhabricatorImageTransformer(); 609 - return $xformer->executeProfileTransform( 610 - $file, 611 - $width = 50, 612 - $min_height = 50, 613 - $max_height = 50); 614 - } catch (Exception $ex) { 615 - phlog($ex); 616 - return null; 617 - } 607 + $xform = PhabricatorFileTransform::getTransformByKey( 608 + PhabricatorFileThumbnailTransform::TRANSFORM_PROFILE); 609 + return $xform->executeTransform($file); 618 610 } 619 611 620 612 protected function renderError($message) {
+1 -166
src/applications/files/PhabricatorImageTransformer.php
··· 20 20 )); 21 21 } 22 22 23 - public function executeProfileTransform( 24 - PhabricatorFile $file, 25 - $x, 26 - $min_y, 27 - $max_y) { 28 - 29 - $image = $this->crudelyCropTo($file, $x, $min_y, $max_y); 30 - 31 - return PhabricatorFile::newFromFileData( 32 - $image, 33 - array( 34 - 'name' => 'profile-'.$file->getName(), 35 - 'canCDN' => true, 36 - )); 37 - } 38 - 39 23 public function executeConpherenceTransform( 40 24 PhabricatorFile $file, 41 25 $top, ··· 54 38 $image, 55 39 array( 56 40 'name' => 'conpherence-'.$file->getName(), 41 + 'profile' => true, 57 42 'canCDN' => true, 58 43 )); 59 44 } 60 45 61 - private function crudelyCropTo(PhabricatorFile $file, $x, $min_y, $max_y) { 62 - $data = $file->loadFileData(); 63 - $img = imagecreatefromstring($data); 64 - $sx = imagesx($img); 65 - $sy = imagesy($img); 66 - 67 - $scaled_y = ($x / $sx) * $sy; 68 - if ($scaled_y > $max_y) { 69 - // This image is very tall and thin. 70 - $scaled_y = $max_y; 71 - } else if ($scaled_y < $min_y) { 72 - // This image is very short and wide. 73 - $scaled_y = $min_y; 74 - } 75 - 76 - $cropped = $this->applyScaleWithImagemagick($file, $x, $scaled_y); 77 - if ($cropped != null) { 78 - return $cropped; 79 - } 80 - 81 - $img = $this->applyScaleTo( 82 - $file, 83 - $x, 84 - $scaled_y); 85 - 86 - return self::saveImageDataInAnyFormat($img, $file->getMimeType()); 87 - } 88 - 89 46 private function crasslyCropTo(PhabricatorFile $file, $top, $left, $w, $h) { 90 47 $data = $file->loadFileData(); 91 48 $src = imagecreatefromstring($data); ··· 114 71 imagefill($dst, 0, 0, imagecolorallocatealpha($dst, 255, 255, 255, 127)); 115 72 116 73 return $dst; 117 - } 118 - 119 - private function applyScaleTo(PhabricatorFile $file, $dx, $dy) { 120 - $data = $file->loadFileData(); 121 - $src = imagecreatefromstring($data); 122 - 123 - $x = imagesx($src); 124 - $y = imagesy($src); 125 - 126 - $scale = min(($dx / $x), ($dy / $y), 1); 127 - 128 - $sdx = $scale * $x; 129 - $sdy = $scale * $y; 130 - 131 - $dst = $this->getBlankDestinationFile($dx, $dy); 132 - imagesavealpha($dst, true); 133 - imagefill($dst, 0, 0, imagecolorallocatealpha($dst, 255, 255, 255, 127)); 134 - 135 - imagecopyresampled( 136 - $dst, 137 - $src, 138 - ($dx - $sdx) / 2, ($dy - $sdy) / 2, 139 - 0, 0, 140 - $sdx, $sdy, 141 - $x, $y); 142 - 143 - return $dst; 144 - 145 74 } 146 75 147 76 public static function getScaleForCrop( ··· 302 231 ); 303 232 } 304 233 305 - private function applyScaleWithImagemagick(PhabricatorFile $file, $dx, $dy) { 306 - $img_type = $file->getMimeType(); 307 - $imagemagick = PhabricatorEnv::getEnvConfig('files.enable-imagemagick'); 308 - 309 - if ($img_type != 'image/gif' || $imagemagick == false) { 310 - return null; 311 - } 312 - 313 - $data = $file->loadFileData(); 314 - $src = imagecreatefromstring($data); 315 - 316 - $x = imagesx($src); 317 - $y = imagesy($src); 318 - 319 - if (self::isEnormousGIF($x, $y)) { 320 - return null; 321 - } 322 - 323 - $scale = min(($dx / $x), ($dy / $y), 1); 324 - 325 - $sdx = $scale * $x; 326 - $sdy = $scale * $y; 327 - 328 - $input = new TempFile(); 329 - Filesystem::writeFile($input, $data); 330 - 331 - $resized = new TempFile(); 332 - 333 - $future = new ExecFuture( 334 - 'convert %s -coalesce -resize %sX%s%s %s', 335 - $input, 336 - $sdx, 337 - $sdy, 338 - '!', 339 - $resized); 340 - 341 - // Don't spend more than 10 seconds resizing; just fail if it takes longer 342 - // than that. 343 - $future->setTimeout(10)->resolvex(); 344 - 345 - return Filesystem::readFile($resized); 346 - } 347 - 348 234 private function applyMemeWithImagemagick( 349 235 $input, 350 236 $above, ··· 380 266 $future->setTimeout(10)->resolvex(); 381 267 382 268 return Filesystem::readFile($output); 383 - } 384 - 385 - /* -( Detecting Enormous Files )------------------------------------------- */ 386 - 387 - 388 - /** 389 - * Determine if an image is enormous (too large to transform). 390 - * 391 - * Attackers can perform a denial of service attack by uploading highly 392 - * compressible images with enormous dimensions but a very small filesize. 393 - * Transforming them (e.g., into thumbnails) may consume huge quantities of 394 - * memory and CPU relative to the resources required to transmit the file. 395 - * 396 - * In general, we respond to these images by declining to transform them, and 397 - * using a default thumbnail instead. 398 - * 399 - * @param int Width of the image, in pixels. 400 - * @param int Height of the image, in pixels. 401 - * @return bool True if this image is enormous (too large to transform). 402 - * @task enormous 403 - */ 404 - public static function isEnormousImage($x, $y) { 405 - // This is just a sanity check, but if we don't have valid dimensions we 406 - // shouldn't be trying to transform the file. 407 - if (($x <= 0) || ($y <= 0)) { 408 - return true; 409 - } 410 - 411 - return ($x * $y) > (4096 * 4096); 412 - } 413 - 414 - 415 - /** 416 - * Determine if a GIF is enormous (too large to transform). 417 - * 418 - * For discussion, see @{method:isEnormousImage}. We need to be more 419 - * careful about GIFs, because they can also have a large number of frames 420 - * despite having a very small filesize. We're more conservative about 421 - * calling GIFs enormous than about calling images in general enormous. 422 - * 423 - * @param int Width of the GIF, in pixels. 424 - * @param int Height of the GIF, in pixels. 425 - * @return bool True if this image is enormous (too large to transform). 426 - * @task enormous 427 - */ 428 - public static function isEnormousGIF($x, $y) { 429 - if (self::isEnormousImage($x, $y)) { 430 - return true; 431 - } 432 - 433 - return ($x * $y) > (800 * 800); 434 269 } 435 270 436 271
+4 -3
src/applications/files/controller/PhabricatorFileComposeController.php
··· 58 58 } 59 59 60 60 $root = dirname(phutil_get_library_root('phabricator')); 61 - $icon_file = $root.'/resources/sprite/projects_1x/'.$icon.'.png'; 61 + $icon_file = $root.'/resources/sprite/projects_2x/'.$icon.'.png'; 62 62 $icon_data = Filesystem::readFile($icon_file); 63 63 64 64 ··· 68 68 $data, 69 69 array( 70 70 'name' => 'project.png', 71 + 'profile' => true, 71 72 'canCDN' => true, 72 73 )); 73 74 ··· 325 326 $color_string = idx($map, $color, '#ff00ff'); 326 327 $color_const = hexdec(trim($color_string, '#')); 327 328 328 - $canvas = imagecreatetruecolor(50, 50); 329 + $canvas = imagecreatetruecolor(100, 100); 329 330 imagefill($canvas, 0, 0, $color_const); 330 331 331 - imagecopy($canvas, $icon_img, 0, 0, 0, 0, 50, 50); 332 + imagecopy($canvas, $icon_img, 0, 0, 0, 0, 100, 100); 332 333 333 334 return PhabricatorImageTransformer::saveImageDataInAnyFormat( 334 335 $canvas,
+6
src/applications/files/controller/PhabricatorFileInfoController.php
··· 247 247 248 248 $finfo->addProperty(pht('Builtin'), $builtin_string); 249 249 250 + $is_profile = $file->getIsProfileImage() 251 + ? pht('Yes') 252 + : pht('No'); 253 + 254 + $finfo->addProperty(pht('Profile'), $is_profile); 255 + 250 256 $storage_properties = new PHUIPropertyListView(); 251 257 $box->addPropertyList($storage_properties, pht('Storage')); 252 258
+18
src/applications/files/storage/PhabricatorFile.php
··· 34 34 const METADATA_CAN_CDN = 'canCDN'; 35 35 const METADATA_BUILTIN = 'builtin'; 36 36 const METADATA_PARTIAL = 'partial'; 37 + const METADATA_PROFILE = 'profile'; 37 38 38 39 protected $name; 39 40 protected $mimeType; ··· 1112 1113 return $this; 1113 1114 } 1114 1115 1116 + public function getIsProfileImage() { 1117 + return idx($this->metadata, self::METADATA_PROFILE); 1118 + } 1119 + 1120 + public function setIsProfileImage($value) { 1121 + $this->metadata[self::METADATA_PROFILE] = $value; 1122 + return $this; 1123 + } 1124 + 1115 1125 protected function generateOneTimeToken() { 1116 1126 $key = Filesystem::readRandomCharacters(16); 1117 1127 ··· 1213 1223 $this->setBuiltinName($builtin); 1214 1224 } 1215 1225 1226 + $profile = idx($params, 'profile'); 1227 + if ($profile) { 1228 + $this->setIsProfileImage(true); 1229 + } 1230 + 1216 1231 $mime_type = idx($params, 'mime-type'); 1217 1232 if ($mime_type) { 1218 1233 $this->setMimeType($mime_type); ··· 1278 1293 switch ($capability) { 1279 1294 case PhabricatorPolicyCapability::CAN_VIEW: 1280 1295 if ($this->isBuiltin()) { 1296 + return PhabricatorPolicies::getMostOpenPolicy(); 1297 + } 1298 + if ($this->getIsProfileImage()) { 1281 1299 return PhabricatorPolicies::getMostOpenPolicy(); 1282 1300 } 1283 1301 return $this->getViewPolicy();
+5 -1
src/applications/files/transform/PhabricatorFileImageTransform.php
··· 38 38 $this->imageY = null; 39 39 } 40 40 41 + protected function getFileProperties() { 42 + return array(); 43 + } 44 + 41 45 protected function applyCropAndScale( 42 46 $dst_w, $dst_h, 43 47 $src_x, $src_y, ··· 144 148 array( 145 149 'name' => $name, 146 150 'canCDN' => true, 147 - )); 151 + ) + $this->getFileProperties()); 148 152 } 149 153 150 154
+10
src/applications/files/transform/PhabricatorFileThumbnailTransform.php
··· 43 43 return $this->key; 44 44 } 45 45 46 + protected function getFileProperties() { 47 + $properties = array(); 48 + switch ($this->key) { 49 + case self::TRANSFORM_PROFILE: 50 + $properties['profile'] = true; 51 + break; 52 + } 53 + return $properties; 54 + } 55 + 46 56 public function generateTransforms() { 47 57 return array( 48 58 id(new PhabricatorFileThumbnailTransform())
+12
src/applications/files/transform/PhabricatorFileTransform.php
··· 15 15 return array($this); 16 16 } 17 17 18 + public function executeTransform(PhabricatorFile $file) { 19 + if ($this->canApplyTransform($file)) { 20 + try { 21 + return $this->applyTransform($file); 22 + } catch (Exception $ex) { 23 + // Ignore. 24 + } 25 + } 26 + 27 + return $this->getDefaultTransform($file); 28 + } 29 + 18 30 public static function getAllTransforms() { 19 31 static $map; 20 32
+3 -6
src/applications/people/controller/PhabricatorPeopleProfilePictureController.php
··· 70 70 'This server only supports these image formats: %s.', 71 71 implode(', ', $supported_formats)); 72 72 } else { 73 - $xformer = new PhabricatorImageTransformer(); 74 - $xformed = $xformer->executeProfileTransform( 75 - $file, 76 - $width = 50, 77 - $min_height = 50, 78 - $max_height = 50); 73 + $xform = PhabricatorFileTransform::getTransformByKey( 74 + PhabricatorFileThumbnailTransform::TRANSFORM_PROFILE); 75 + $xformed = $xform->executeTransform($file); 79 76 } 80 77 } 81 78
+3 -6
src/applications/project/controller/PhabricatorProjectEditPictureController.php
··· 68 68 'This server only supports these image formats: %s.', 69 69 implode(', ', $supported_formats)); 70 70 } else { 71 - $xformer = new PhabricatorImageTransformer(); 72 - $xformed = $xformer->executeProfileTransform( 73 - $file, 74 - $width = 50, 75 - $min_height = 50, 76 - $max_height = 50); 71 + $xform = PhabricatorFileTransform::getTransformByKey( 72 + PhabricatorFileThumbnailTransform::TRANSFORM_PROFILE); 73 + $xformed = $xform->executeTransform($file); 77 74 } 78 75 } 79 76
+1
webroot/rsrc/css/phui/phui-header-view.css
··· 90 90 .phui-header-image { 91 91 display: inline-block; 92 92 background-repeat: no-repeat; 93 + background-size: 50px; 93 94 border: 2px solid white; 94 95 width: 50px; 95 96 height: 50px;
+1
webroot/rsrc/css/phui/phui-timeline-view.css
··· 92 92 93 93 .phui-timeline-image { 94 94 background-repeat: no-repeat; 95 + background-size: 50px; 95 96 position: absolute; 96 97 border-radius: 3px; 97 98 }