@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 "profile" image transforms to the new pathway

Summary:
Ref T7707. This ends up being sort of complicated: to support 100x100 images in T4406, we need to scale small images //up// so they look OK when we scale them back down with `background-size` in CSS.

The rest of it is mostly straightforward.

Test Plan:
- Did an OAuth handshake and saw a scaled-up, scaled-down profile picture that looked correct.
- Used Pholio, edited pholio, embedded pholio.
- Uploaded a bunch of small/weird/big images and regenerated all their transforms.
- Uploaded some text files into Pholio.
- Grepped for removed methods, etc.

Reviewers: chad, btrahan

Reviewed By: btrahan

Subscribers: epriestley

Maniphest Tasks: T7707

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

+115 -141
+6 -2
src/applications/auth/view/PhabricatorAuthAccountView.php
··· 89 89 $account_uri); 90 90 } 91 91 92 - $image_uri = $account->getProfileImageFile()->getProfileThumbURI(); 92 + $image_file = $account->getProfileImageFile(); 93 + $xform = PhabricatorFileTransform::getTransformByKey( 94 + PhabricatorFileThumbnailTransform::TRANSFORM_PROFILE); 95 + $image_uri = $image_file->getURIForTransform($xform); 96 + list($x, $y) = $xform->getTransformedDimensions($image_file); 93 97 94 98 return phutil_tag( 95 99 'div', 96 100 array( 97 101 'class' => 'auth-account-view', 98 - 'style' => 'background-image: url('.$image_uri.')', 102 + 'style' => 'background-image: url('.$image_uri.');', 99 103 ), 100 104 $content); 101 105 }
-30
src/applications/files/PhabricatorImageTransformer.php
··· 20 20 )); 21 21 } 22 22 23 - public function executeThumbTransform( 24 - PhabricatorFile $file, 25 - $x, 26 - $y) { 27 - 28 - $image = $this->crudelyScaleTo($file, $x, $y); 29 - 30 - return PhabricatorFile::newFromFileData( 31 - $image, 32 - array( 33 - 'name' => 'thumb-'.$file->getName(), 34 - 'canCDN' => true, 35 - )); 36 - } 37 - 38 23 public function executeProfileTransform( 39 24 PhabricatorFile $file, 40 25 $x, ··· 120 105 $w, $h, 121 106 $orig_w, $orig_h); 122 107 123 - return self::saveImageDataInAnyFormat($dst, $file->getMimeType()); 124 - } 125 - 126 - 127 - /** 128 - * Very crudely scale an image up or down to an exact size. 129 - */ 130 - private function crudelyScaleTo(PhabricatorFile $file, $dx, $dy) { 131 - $scaled = $this->applyScaleWithImagemagick($file, $dx, $dy); 132 - 133 - if ($scaled != null) { 134 - return $scaled; 135 - } 136 - 137 - $dst = $this->applyScaleTo($file, $dx, $dy); 138 108 return self::saveImageDataInAnyFormat($dst, $file->getMimeType()); 139 109 } 140 110
+15 -71
src/applications/files/controller/PhabricatorFileTransformController.php
··· 44 44 } 45 45 } 46 46 47 - $type = $file->getMimeType(); 47 + $xforms = PhabricatorFileTransform::getAllTransforms(); 48 + if (!isset($xforms[$transform])) { 49 + return new Aphront404Response(); 50 + } 48 51 49 - if (!$file->isViewableInBrowser() || !$file->isTransformableImage()) { 50 - return $this->buildDefaultTransformation($file, $transform); 51 - } 52 + $xform = $xforms[$transform]; 52 53 53 54 // We're essentially just building a cache here and don't need CSRF 54 55 // protection. 55 56 $unguarded = AphrontWriteGuard::beginScopedUnguardedWrites(); 56 57 57 58 $xformed_file = null; 58 - 59 - $xforms = PhabricatorFileTransform::getAllTransforms(); 60 - if (isset($xforms[$transform])) { 61 - $xform = $xforms[$transform]; 62 - if ($xform->canApplyTransform($file)) { 63 - try { 64 - $xformed_file = $xforms[$transform]->applyTransform($file); 65 - } catch (Exception $ex) { 66 - // In normal transform mode, we ignore failures and generate a 67 - // default transform below. If we're explicitly regenerating the 68 - // thumbnail, rethrow the exception. 69 - if ($is_regenerate) { 70 - throw $ex; 71 - } 59 + if ($xform->canApplyTransform($file)) { 60 + try { 61 + $xformed_file = $xforms[$transform]->applyTransform($file); 62 + } catch (Exception $ex) { 63 + // In normal transform mode, we ignore failures and generate a 64 + // default transform below. If we're explicitly regenerating the 65 + // thumbnail, rethrow the exception. 66 + if ($is_regenerate) { 67 + throw $ex; 72 68 } 73 69 } 74 - 75 - if (!$xformed_file) { 76 - $xformed_file = $xform->getDefaultTransform($file); 77 - } 78 70 } 79 71 80 72 if (!$xformed_file) { 81 - switch ($transform) { 82 - case 'thumb-profile': 83 - $xformed_file = $this->executeThumbTransform($file, 50, 50); 84 - break; 85 - case 'thumb-280x210': 86 - $xformed_file = $this->executeThumbTransform($file, 280, 210); 87 - break; 88 - default: 89 - return new Aphront400Response(); 90 - } 73 + $xformed_file = $xform->getDefaultTransform($file); 91 74 } 92 75 93 76 if (!$xformed_file) { ··· 103 86 return $this->buildTransformedFileResponse($xform); 104 87 } 105 88 106 - private function buildDefaultTransformation( 107 - PhabricatorFile $file, 108 - $transform) { 109 - static $regexps = array( 110 - '@application/zip@' => 'zip', 111 - '@image/@' => 'image', 112 - '@application/pdf@' => 'pdf', 113 - '@.*@' => 'default', 114 - ); 115 - 116 - $type = $file->getMimeType(); 117 - $prefix = 'default'; 118 - foreach ($regexps as $regexp => $implied_prefix) { 119 - if (preg_match($regexp, $type)) { 120 - $prefix = $implied_prefix; 121 - break; 122 - } 123 - } 124 - 125 - switch ($transform) { 126 - case 'thumb-280x210': 127 - $suffix = '280x210'; 128 - break; 129 - default: 130 - throw new Exception('Unsupported transformation type!'); 131 - } 132 - 133 - $path = celerity_get_resource_uri( 134 - "rsrc/image/icon/fatcow/thumbnails/{$prefix}{$suffix}.png"); 135 - 136 - return id(new AphrontRedirectResponse()) 137 - ->setURI($path); 138 - } 139 - 140 89 private function buildTransformedFileResponse( 141 90 PhabricatorTransformedFile $xform) { 142 91 ··· 152 101 // which would save the client a roundtrip, but is slightly more complex. 153 102 154 103 return $file->getRedirectResponse(); 155 - } 156 - 157 - private function executeThumbTransform(PhabricatorFile $file, $x, $y) { 158 - $xformer = new PhabricatorImageTransformer(); 159 - return $xformer->executeThumbTransform($file, $x, $y); 160 104 } 161 105 162 106 private function destroyTransform(PhabricatorTransformedFile $xform) {
-8
src/applications/files/storage/PhabricatorFile.php
··· 784 784 return PhabricatorEnv::getCDNURI($path); 785 785 } 786 786 787 - public function getProfileThumbURI() { 788 - return $this->getTransformedURI('thumb-profile'); 789 - } 790 - 791 - public function getThumb280x210URI() { 792 - return $this->getTransformedURI('thumb-280x210'); 793 - } 794 - 795 787 public function isViewableInBrowser() { 796 788 return ($this->getViewableMimeType() !== null); 797 789 }
+28 -8
src/applications/files/transform/PhabricatorFileImageTransform.php
··· 42 42 $dst_w, $dst_h, 43 43 $src_x, $src_y, 44 44 $src_w, $src_h, 45 - $use_w, $use_h) { 45 + $use_w, $use_h, 46 + $scale_up) { 46 47 47 - // Figure out the effective destination width, height, and offsets. We 48 - // never want to scale images up, so if we're copying a very small source 49 - // image we're just going to center it in the destination image. 50 - $cpy_w = min($dst_w, $src_w, $use_w); 51 - $cpy_h = min($dst_h, $src_h, $use_h); 48 + // Figure out the effective destination width, height, and offsets. 49 + $cpy_w = min($dst_w, $use_w); 50 + $cpy_h = min($dst_h, $use_h); 51 + 52 + // If we aren't scaling up, and are copying a very small source image, 53 + // we're just going to center it in the destination image. 54 + if (!$scale_up) { 55 + $cpy_w = min($cpy_w, $src_w); 56 + $cpy_h = min($cpy_h, $src_h); 57 + } 58 + 52 59 $off_x = ($dst_w - $cpy_w) / 2; 53 60 $off_y = ($dst_h - $cpy_h) / 2; 54 61 ··· 58 65 $argv[] = '-shave'; 59 66 $argv[] = $src_x.'x'.$src_y; 60 67 $argv[] = '-resize'; 61 - $argv[] = $dst_w.'x'.$dst_h.'>'; 68 + 69 + if ($scale_up) { 70 + $argv[] = $dst_w.'x'.$dst_h; 71 + } else { 72 + $argv[] = $dst_w.'x'.$dst_h.'>'; 73 + } 74 + 62 75 $argv[] = '-bordercolor'; 63 76 $argv[] = 'rgba(255, 255, 255, 0)'; 64 77 $argv[] = '-border'; 65 78 $argv[] = $off_x.'x'.$off_y; 79 + 66 80 return $this->applyImagemagick($argv); 67 81 } 68 82 ··· 117 131 * @param string Raw file data. 118 132 */ 119 133 protected function newFileFromData($data) { 120 - $name = $this->getTransformKey().'-'.$this->file->getName(); 134 + if ($this->file) { 135 + $name = $this->file->getName(); 136 + } else { 137 + $name = 'default.png'; 138 + } 139 + 140 + $name = $this->getTransformKey().'-'.$name; 121 141 122 142 return PhabricatorFile::newFromFileData( 123 143 $data,
+29 -8
src/applications/files/transform/PhabricatorFileThumbnailTransform.php
··· 12 12 private $key; 13 13 private $dstX; 14 14 private $dstY; 15 + private $scaleUp; 15 16 16 17 public function setName($name) { 17 18 $this->name = $name; ··· 29 30 return $this; 30 31 } 31 32 33 + public function setScaleUp($scale) { 34 + $this->scaleUp = $scale; 35 + return $this; 36 + } 37 + 32 38 public function getTransformName() { 33 39 return $this->name; 34 40 } ··· 42 48 id(new PhabricatorFileThumbnailTransform()) 43 49 ->setName(pht("Profile (100px \xC3\x97 100px)")) 44 50 ->setKey(self::TRANSFORM_PROFILE) 45 - ->setDimensions(100, 100), 51 + ->setDimensions(100, 100) 52 + ->setScaleUp(true), 46 53 id(new PhabricatorFileThumbnailTransform()) 47 54 ->setName(pht("Pinboard (280px \xC3\x97 210px)")) 48 55 ->setKey(self::TRANSFORM_PINBOARD) ··· 86 93 $copy_x, 87 94 $copy_y, 88 95 $use_x, 89 - $use_y); 96 + $use_y, 97 + $this->scaleUp); 90 98 } 91 99 92 100 ··· 144 152 $copy_x = $src_x; 145 153 $copy_y = $src_y; 146 154 } else { 155 + $scale_up = $this->scaleUp; 156 + 147 157 // Otherwise, both dimensions are fixed. Figure out how much we'd have to 148 158 // scale the image down along each dimension to get the entire thing to 149 159 // fit. 150 - $scale_x = min(($dst_x / $src_x), 1); 151 - $scale_y = min(($dst_y / $src_y), 1); 160 + $scale_x = ($dst_x / $src_x); 161 + $scale_y = ($dst_y / $src_y); 162 + 163 + if (!$scale_up) { 164 + $scale_x = min($scale_x, 1); 165 + $scale_y = min($scale_y, 1); 166 + } 152 167 153 168 if ($scale_x > $scale_y) { 154 169 // This image is relatively tall and narrow. We're going to crop off the 155 170 // top and bottom. 156 - $copy_x = $src_x; 157 - $copy_y = min($src_y, $dst_y / $scale_x); 171 + $scale = $scale_x; 158 172 } else { 159 173 // This image is relatively short and wide. We're going to crop off the 160 174 // left and right. 161 - $copy_x = min($src_x, $dst_x / $scale_y); 162 - $copy_y = $src_y; 175 + $scale = $scale_y; 176 + } 177 + 178 + $copy_x = $dst_x / $scale_x; 179 + $copy_y = $dst_y / $scale_x; 180 + 181 + if (!$scale_up) { 182 + $copy_x = min($src_x, $copy_x); 183 + $copy_y = min($src_y, $copy_y); 163 184 } 164 185 165 186 // In this mode, we always use the entire destination image. We may
+6 -2
src/applications/macro/query/PhabricatorMacroSearchEngine.php
··· 179 179 assert_instances_of($macros, 'PhabricatorFileImageMacro'); 180 180 $viewer = $this->requireViewer(); 181 181 182 + $xform = PhabricatorFileTransform::getTransformByKey( 183 + PhabricatorFileThumbnailTransform::TRANSFORM_PINBOARD); 184 + 182 185 $pinboard = new PHUIPinboardView(); 183 186 foreach ($macros as $macro) { 184 187 $file = $macro->getFile(); 185 188 186 189 $item = new PHUIPinboardItemView(); 187 190 if ($file) { 188 - $item->setImageURI($file->getThumb280x210URI()); 189 - $item->setImageSize(280, 210); 191 + $item->setImageURI($file->getURIForTransform($xform)); 192 + list($x, $y) = $xform->getTransformedDimensions($file); 193 + $item->setImageSize($x, $y); 190 194 } 191 195 192 196 if ($macro->getDateCreated()) {
+9 -2
src/applications/pholio/query/PholioMockSearchEngine.php
··· 141 141 142 142 $viewer = $this->requireViewer(); 143 143 144 + $xform = PhabricatorFileTransform::getTransformByKey( 145 + PhabricatorFileThumbnailTransform::TRANSFORM_PINBOARD); 146 + 144 147 $board = new PHUIPinboardView(); 145 148 foreach ($mocks as $mock) { 146 149 150 + $image = $mock->getCoverFile(); 151 + $image_uri = $image->getURIForTransform($xform); 152 + list($x, $y) = $xform->getTransformedDimensions($image); 153 + 147 154 $header = 'M'.$mock->getID().' '.$mock->getName(); 148 155 $item = id(new PHUIPinboardItemView()) 149 156 ->setHeader($header) 150 157 ->setURI('/M'.$mock->getID()) 151 - ->setImageURI($mock->getCoverFile()->getThumb280x210URI()) 152 - ->setImageSize(280, 210) 158 + ->setImageURI($image_uri) 159 + ->setImageSize($x, $y) 153 160 ->setDisabled($mock->isClosed()) 154 161 ->addIconCount('fa-picture-o', count($mock->getImages())) 155 162 ->addIconCount('fa-trophy', $mock->getTokenCount());
+10 -6
src/applications/pholio/view/PholioMockEmbedView.php
··· 28 28 $this->mock->getImages(), array_flip($this->images)); 29 29 } 30 30 31 + $xform = PhabricatorFileTransform::getTransformByKey( 32 + PhabricatorFileThumbnailTransform::TRANSFORM_PINBOARD); 33 + 31 34 if ($images_to_show) { 32 - foreach ($images_to_show as $image) { 33 - $thumbfile = $image->getFile(); 34 - $thumbnail = $thumbfile->getThumb280x210URI(); 35 - } 35 + $image = head($images_to_show); 36 + $thumbfile = $image->getFile(); 36 37 $header = 'M'.$mock->getID().' '.$mock->getName(). 37 38 ' (#'.$image->getID().')'; 38 39 $uri = '/M'.$this->mock->getID().'/'.$image->getID().'/'; 39 40 } else { 40 - $thumbnail = $mock->getCoverFile()->getThumb280x210URI(); 41 + $thumbfile = $mock->getCoverFile(); 41 42 $header = 'M'.$mock->getID().' '.$mock->getName(); 42 43 $uri = '/M'.$this->mock->getID(); 43 44 } 44 45 46 + $thumbnail = $thumbfile->getURIForTransform($xform); 47 + list($x, $y) = $xform->getTransformedDimensions($thumbfile); 48 + 45 49 $item = id(new PHUIPinboardItemView()) 46 50 ->setHeader($header) 47 51 ->setURI($uri) 48 52 ->setImageURI($thumbnail) 49 - ->setImageSize(280, 210) 53 + ->setImageSize($x, $y) 50 54 ->setDisabled($mock->isClosed()) 51 55 ->addIconCount('fa-picture-o', count($mock->getImages())) 52 56 ->addIconCount('fa-trophy', $mock->getTokenCount());
+6 -3
src/applications/pholio/view/PholioMockImagesView.php
··· 68 68 69 69 // TODO: We could maybe do a better job with tailoring this, which is the 70 70 // image shown on the review stage. 71 - $nonimage_uri = celerity_get_resource_uri( 72 - 'rsrc/image/icon/fatcow/thumbnails/default.p100.png'); 71 + $default_name = 'image-100x100.png'; 72 + $builtins = PhabricatorFile::loadBuiltins( 73 + $this->getUser(), 74 + array($default_name)); 75 + $default = $builtins[$default_name]; 73 76 74 77 $engine = id(new PhabricatorMarkupEngine()) 75 78 ->setViewer($this->getUser()); ··· 97 100 'fullURI' => $file->getBestURI(), 98 101 'stageURI' => ($file->isViewableImage() 99 102 ? $file->getBestURI() 100 - : $nonimage_uri), 103 + : $default->getBestURI()), 101 104 'pageURI' => $this->getImagePageURI($image, $mock), 102 105 'downloadURI' => $file->getDownloadURI(), 103 106 'historyURI' => $history_uri,
+5 -1
src/applications/pholio/view/PholioUploadedImageView.php
··· 38 38 ->setSigil('image-description') 39 39 ->setLabel(pht('Description')); 40 40 41 + $xform = PhabricatorFileTransform::getTransformByKey( 42 + PhabricatorFileThumbnailTransform::TRANSFORM_PINBOARD); 43 + $thumbnail_uri = $file->getURIForTransform($xform); 44 + 41 45 $thumb_frame = phutil_tag( 42 46 'div', 43 47 array( 44 48 'class' => 'pholio-thumb-frame', 45 - 'style' => 'background-image: url('.$file->getThumb280x210URI().');', 49 + 'style' => 'background-image: url('.$thumbnail_uri.');', 46 50 )); 47 51 48 52 $handle = javelin_tag(
+1
webroot/rsrc/css/application/auth/auth.css
··· 28 28 border: 1px solid {$lightblueborder}; 29 29 background-repeat: no-repeat; 30 30 background-position: 4px 4px; 31 + background-size: 50px 50px; 31 32 padding: 4px 4px 4px 62px; 32 33 min-height: 50px; 33 34 border-radius: 2px;
webroot/rsrc/image/icon/fatcow/thumbnails/default280x210.png

This is a binary file and will not be displayed.

webroot/rsrc/image/icon/fatcow/thumbnails/image280x210.png

This is a binary file and will not be displayed.

webroot/rsrc/image/icon/fatcow/thumbnails/pdf280x210.png

This is a binary file and will not be displayed.

webroot/rsrc/image/icon/fatcow/thumbnails/zip280x210.png

This is a binary file and will not be displayed.