@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.

Allow custom image generation when choosing a profile image

Summary: Ref T10319. This swaps the default in the Picture Chooser to allow picking of the custom unique avatar. We're currently going with 100k unique possibilities. The logic roughly hashes a user name and picks an image pack, color, and border. Based on that, we select the first character of their username, or fall back to Psyduck if not [a-z][0-9].

Test Plan:
Set the following usernames from ProfilePicture as a test: chad, epriestley, sally, 007, _cat_, -doggie-.

{F3453979}

Reviewers: epriestley

Reviewed By: epriestley

Subscribers: Korvin

Maniphest Tasks: T10319

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

+212 -52
resources/builtin/alphanumeric/0.png resources/builtin/alphanumeric/lato-white/0.png

This is a binary file and will not be displayed.

resources/builtin/alphanumeric/0d.png resources/builtin/alphanumeric/lato-dark/0.png
resources/builtin/alphanumeric/1.png resources/builtin/alphanumeric/lato-white/1.png

This is a binary file and will not be displayed.

resources/builtin/alphanumeric/1d.png resources/builtin/alphanumeric/lato-dark/1.png
resources/builtin/alphanumeric/2.png resources/builtin/alphanumeric/lato-white/2.png

This is a binary file and will not be displayed.

resources/builtin/alphanumeric/2d.png resources/builtin/alphanumeric/lato-dark/2.png
resources/builtin/alphanumeric/3.png resources/builtin/alphanumeric/lato-white/3.png

This is a binary file and will not be displayed.

resources/builtin/alphanumeric/3d.png resources/builtin/alphanumeric/lato-dark/3.png
resources/builtin/alphanumeric/4.png resources/builtin/alphanumeric/lato-white/4.png

This is a binary file and will not be displayed.

resources/builtin/alphanumeric/4d.png resources/builtin/alphanumeric/lato-dark/4.png
resources/builtin/alphanumeric/5.png resources/builtin/alphanumeric/lato-white/5.png

This is a binary file and will not be displayed.

resources/builtin/alphanumeric/5d.png resources/builtin/alphanumeric/lato-dark/5.png
resources/builtin/alphanumeric/6.png resources/builtin/alphanumeric/lato-white/6.png

This is a binary file and will not be displayed.

resources/builtin/alphanumeric/6d.png resources/builtin/alphanumeric/lato-dark/6.png
resources/builtin/alphanumeric/7.png resources/builtin/alphanumeric/lato-white/7.png

This is a binary file and will not be displayed.

resources/builtin/alphanumeric/7d.png resources/builtin/alphanumeric/lato-dark/7.png
resources/builtin/alphanumeric/8.png resources/builtin/alphanumeric/lato-white/8.png

This is a binary file and will not be displayed.

resources/builtin/alphanumeric/8d.png resources/builtin/alphanumeric/lato-dark/8.png
resources/builtin/alphanumeric/9.png resources/builtin/alphanumeric/lato-white/9.png

This is a binary file and will not be displayed.

resources/builtin/alphanumeric/9d.png resources/builtin/alphanumeric/lato-dark/9.png
resources/builtin/alphanumeric/A.png resources/builtin/alphanumeric/lato-white/A.png

This is a binary file and will not be displayed.

resources/builtin/alphanumeric/Ad.png resources/builtin/alphanumeric/lato-dark/A.png

This is a binary file and will not be displayed.

resources/builtin/alphanumeric/B.png resources/builtin/alphanumeric/lato-white/B.png

This is a binary file and will not be displayed.

resources/builtin/alphanumeric/Bd.png resources/builtin/alphanumeric/lato-dark/B.png
resources/builtin/alphanumeric/C.png resources/builtin/alphanumeric/lato-white/C.png

This is a binary file and will not be displayed.

resources/builtin/alphanumeric/Cd.png

This is a binary file and will not be displayed.

resources/builtin/alphanumeric/D.png resources/builtin/alphanumeric/lato-white/D.png

This is a binary file and will not be displayed.

resources/builtin/alphanumeric/Dd.png

This is a binary file and will not be displayed.

resources/builtin/alphanumeric/E.png resources/builtin/alphanumeric/lato-white/E.png

This is a binary file and will not be displayed.

resources/builtin/alphanumeric/Ed.png resources/builtin/alphanumeric/lato-dark/E.png
resources/builtin/alphanumeric/F.png resources/builtin/alphanumeric/lato-white/F.png

This is a binary file and will not be displayed.

resources/builtin/alphanumeric/Fd.png resources/builtin/alphanumeric/lato-dark/F.png
resources/builtin/alphanumeric/G.png resources/builtin/alphanumeric/lato-white/G.png

This is a binary file and will not be displayed.

resources/builtin/alphanumeric/Gd.png

This is a binary file and will not be displayed.

resources/builtin/alphanumeric/H.png resources/builtin/alphanumeric/lato-white/H.png

This is a binary file and will not be displayed.

resources/builtin/alphanumeric/Hd.png resources/builtin/alphanumeric/lato-dark/H.png
resources/builtin/alphanumeric/I.png

This is a binary file and will not be displayed.

resources/builtin/alphanumeric/Id.png

This is a binary file and will not be displayed.

resources/builtin/alphanumeric/J.png

This is a binary file and will not be displayed.

resources/builtin/alphanumeric/Jd.png resources/builtin/alphanumeric/lato-dark/J.png
resources/builtin/alphanumeric/K.png resources/builtin/alphanumeric/lato-white/K.png

This is a binary file and will not be displayed.

resources/builtin/alphanumeric/Kd.png resources/builtin/alphanumeric/lato-dark/K.png
resources/builtin/alphanumeric/L.png

This is a binary file and will not be displayed.

resources/builtin/alphanumeric/Ld.png resources/builtin/alphanumeric/lato-dark/L.png
resources/builtin/alphanumeric/M.png

This is a binary file and will not be displayed.

resources/builtin/alphanumeric/Md.png

This is a binary file and will not be displayed.

resources/builtin/alphanumeric/N.png resources/builtin/alphanumeric/lato-dark/N.png

This is a binary file and will not be displayed.

resources/builtin/alphanumeric/Nd.png resources/builtin/alphanumeric/lato-white/N.png

This is a binary file and will not be displayed.

resources/builtin/alphanumeric/O.png resources/builtin/alphanumeric/lato-white/O.png

This is a binary file and will not be displayed.

resources/builtin/alphanumeric/Od.png resources/builtin/alphanumeric/lato-dark/O.png
resources/builtin/alphanumeric/P.png resources/builtin/alphanumeric/lato-white/P.png

This is a binary file and will not be displayed.

resources/builtin/alphanumeric/Pd.png resources/builtin/alphanumeric/lato-dark/P.png
resources/builtin/alphanumeric/Q.png resources/builtin/alphanumeric/lato-white/Q.png

This is a binary file and will not be displayed.

resources/builtin/alphanumeric/Qd.png resources/builtin/alphanumeric/lato-dark/Q.png
resources/builtin/alphanumeric/R.png

This is a binary file and will not be displayed.

resources/builtin/alphanumeric/Rd.png

This is a binary file and will not be displayed.

resources/builtin/alphanumeric/S.png resources/builtin/alphanumeric/lato-white/S.png

This is a binary file and will not be displayed.

resources/builtin/alphanumeric/Sd.png resources/builtin/alphanumeric/lato-dark/S.png
resources/builtin/alphanumeric/Shrug.png

This is a binary file and will not be displayed.

resources/builtin/alphanumeric/Shrug2.png

This is a binary file and will not be displayed.

resources/builtin/alphanumeric/T.png resources/builtin/alphanumeric/lato-white/T.png

This is a binary file and will not be displayed.

resources/builtin/alphanumeric/Td.png resources/builtin/alphanumeric/lato-dark/T.png
resources/builtin/alphanumeric/U.png resources/builtin/alphanumeric/lato-white/U.png

This is a binary file and will not be displayed.

resources/builtin/alphanumeric/Ud.png

This is a binary file and will not be displayed.

resources/builtin/alphanumeric/V.png

This is a binary file and will not be displayed.

resources/builtin/alphanumeric/Vd.png

This is a binary file and will not be displayed.

resources/builtin/alphanumeric/W.png resources/builtin/alphanumeric/lato-white/W.png

This is a binary file and will not be displayed.

resources/builtin/alphanumeric/Wd.png resources/builtin/alphanumeric/lato-dark/W.png
resources/builtin/alphanumeric/X.png resources/builtin/alphanumeric/lato-white/X.png

This is a binary file and will not be displayed.

resources/builtin/alphanumeric/Xd.png resources/builtin/alphanumeric/lato-dark/X.png
resources/builtin/alphanumeric/Y.png resources/builtin/alphanumeric/lato-white/Y.png

This is a binary file and will not be displayed.

resources/builtin/alphanumeric/Yd.png resources/builtin/alphanumeric/lato-dark/Y.png
resources/builtin/alphanumeric/Z.png resources/builtin/alphanumeric/lato-white/Z.png

This is a binary file and will not be displayed.

resources/builtin/alphanumeric/Zd.png resources/builtin/alphanumeric/lato-dark/Z.png
resources/builtin/alphanumeric/aleo-white/0.png

This is a binary file and will not be displayed.

resources/builtin/alphanumeric/aleo-white/1.png

This is a binary file and will not be displayed.

resources/builtin/alphanumeric/aleo-white/2.png

This is a binary file and will not be displayed.

resources/builtin/alphanumeric/aleo-white/3.png

This is a binary file and will not be displayed.

resources/builtin/alphanumeric/aleo-white/4.png

This is a binary file and will not be displayed.

resources/builtin/alphanumeric/aleo-white/5.png

This is a binary file and will not be displayed.

resources/builtin/alphanumeric/aleo-white/6.png

This is a binary file and will not be displayed.

resources/builtin/alphanumeric/aleo-white/7.png

This is a binary file and will not be displayed.

resources/builtin/alphanumeric/aleo-white/8.png

This is a binary file and will not be displayed.

resources/builtin/alphanumeric/aleo-white/9.png

This is a binary file and will not be displayed.

resources/builtin/alphanumeric/aleo-white/A.png

This is a binary file and will not be displayed.

resources/builtin/alphanumeric/aleo-white/B.png

This is a binary file and will not be displayed.

resources/builtin/alphanumeric/aleo-white/C.png

This is a binary file and will not be displayed.

resources/builtin/alphanumeric/aleo-white/D.png

This is a binary file and will not be displayed.

resources/builtin/alphanumeric/aleo-white/E.png

This is a binary file and will not be displayed.

resources/builtin/alphanumeric/aleo-white/F.png

This is a binary file and will not be displayed.

resources/builtin/alphanumeric/aleo-white/G.png

This is a binary file and will not be displayed.

resources/builtin/alphanumeric/aleo-white/H.png

This is a binary file and will not be displayed.

resources/builtin/alphanumeric/aleo-white/I.png

This is a binary file and will not be displayed.

resources/builtin/alphanumeric/aleo-white/J.png

This is a binary file and will not be displayed.

resources/builtin/alphanumeric/aleo-white/K.png

This is a binary file and will not be displayed.

resources/builtin/alphanumeric/aleo-white/L.png

This is a binary file and will not be displayed.

resources/builtin/alphanumeric/aleo-white/M.png

This is a binary file and will not be displayed.

resources/builtin/alphanumeric/aleo-white/N.png

This is a binary file and will not be displayed.

resources/builtin/alphanumeric/aleo-white/O.png

This is a binary file and will not be displayed.

resources/builtin/alphanumeric/aleo-white/P.png

This is a binary file and will not be displayed.

resources/builtin/alphanumeric/aleo-white/Q.png

This is a binary file and will not be displayed.

resources/builtin/alphanumeric/aleo-white/R.png

This is a binary file and will not be displayed.

resources/builtin/alphanumeric/aleo-white/S.png

This is a binary file and will not be displayed.

resources/builtin/alphanumeric/aleo-white/T.png

This is a binary file and will not be displayed.

resources/builtin/alphanumeric/aleo-white/U.png

This is a binary file and will not be displayed.

resources/builtin/alphanumeric/aleo-white/V.png

This is a binary file and will not be displayed.

resources/builtin/alphanumeric/aleo-white/W.png

This is a binary file and will not be displayed.

resources/builtin/alphanumeric/aleo-white/X.png

This is a binary file and will not be displayed.

resources/builtin/alphanumeric/aleo-white/Y.png

This is a binary file and will not be displayed.

resources/builtin/alphanumeric/aleo-white/Z.png

This is a binary file and will not be displayed.

resources/builtin/alphanumeric/aleo-white/_default.png

This is a binary file and will not be displayed.

resources/builtin/alphanumeric/lato-dark/C.png

This is a binary file and will not be displayed.

resources/builtin/alphanumeric/lato-dark/D.png

This is a binary file and will not be displayed.

resources/builtin/alphanumeric/lato-dark/G.png

This is a binary file and will not be displayed.

resources/builtin/alphanumeric/lato-dark/I.png

This is a binary file and will not be displayed.

resources/builtin/alphanumeric/lato-dark/M.png

This is a binary file and will not be displayed.

resources/builtin/alphanumeric/lato-dark/R.png

This is a binary file and will not be displayed.

resources/builtin/alphanumeric/lato-dark/U.png

This is a binary file and will not be displayed.

resources/builtin/alphanumeric/lato-dark/V.png

This is a binary file and will not be displayed.

resources/builtin/alphanumeric/lato-dark/_default.png

This is a binary file and will not be displayed.

resources/builtin/alphanumeric/lato-white/I.png

This is a binary file and will not be displayed.

resources/builtin/alphanumeric/lato-white/J.png

This is a binary file and will not be displayed.

resources/builtin/alphanumeric/lato-white/L.png

This is a binary file and will not be displayed.

resources/builtin/alphanumeric/lato-white/M.png

This is a binary file and will not be displayed.

resources/builtin/alphanumeric/lato-white/R.png

This is a binary file and will not be displayed.

resources/builtin/alphanumeric/lato-white/V.png

This is a binary file and will not be displayed.

resources/builtin/alphanumeric/lato-white/_default.png

This is a binary file and will not be displayed.

+139 -43
src/applications/files/builtin/PhabricatorFilesComposeAvatarBuiltinFile.php
··· 37 37 public function getBuiltinFileKey() { 38 38 $icon = $this->getIcon(); 39 39 $color = $this->getColor(); 40 - $border = $this->getBorder(); 40 + $border = implode(',', $this->getBorder()); 41 41 $desc = "compose(icon={$icon}, color={$color}, border={$border}"; 42 42 $hash = PhabricatorHash::digestToLength($desc, 40); 43 43 return "builtin:{$hash}"; ··· 46 46 public function getBuiltinDisplayName() { 47 47 $icon = $this->getIcon(); 48 48 $color = $this->getColor(); 49 - $border = $this->getBorder(); 49 + $border = implode(',', $this->getBorder()); 50 50 return "{$icon}-{$color}-{$border}.png"; 51 51 } 52 52 ··· 55 55 $this->getColor(), $this->getIcon(), $this->getBorder()); 56 56 } 57 57 58 - private function composeImage($color, $icon, $border) { 59 - // TODO 58 + private function composeImage($color, $image, $border) { 59 + $color_const = hexdec(trim($color, '#')); 60 + $true_border = self::rgba2gd($border); 61 + $image_map = self::getImageMap(); 62 + $data = Filesystem::readFile($image_map[$image]); 63 + 64 + $img = imagecreatefromstring($data); 65 + 66 + // 4 pixel border at 50x50, 32 pixel border at 400x400 67 + $canvas = imagecreatetruecolor(400, 400); 68 + 69 + $image_fill = imagefill($canvas, 0, 0, $color_const); 70 + if (!$image_fill) { 71 + throw new Exception( 72 + pht('Failed to save builtin avatar image data (imagefill).')); 73 + } 74 + 75 + $border_thickness = imagesetthickness($canvas, 64); 76 + if (!$border_thickness) { 77 + throw new Exception( 78 + pht('Failed to save builtin avatar image data (imagesetthickness).')); 79 + } 80 + 81 + $image_rectangle = imagerectangle($canvas, 0, 0, 400, 400, $true_border); 82 + if (!$image_rectangle) { 83 + throw new Exception( 84 + pht('Failed to save builtin avatar image data (imagerectangle).')); 85 + } 86 + 87 + $image_copy = imagecopy($canvas, $img, 0, 0, 0, 0, 400, 400); 88 + if (!$image_copy) { 89 + throw new Exception( 90 + pht('Failed to save builtin avatar image data (imagecopy).')); 91 + } 92 + 93 + return PhabricatorImageTransformer::saveImageDataInAnyFormat( 94 + $canvas, 95 + 'image/png'); 96 + } 97 + 98 + private static function rgba2gd($rgba) { 99 + $r = $rgba[0]; 100 + $g = $rgba[1]; 101 + $b = $rgba[2]; 102 + $a = $rgba[3]; 103 + $a = (1 - $a) * 255; 104 + return ($a << 24) | ($r << 16) | ($g << 8) | $b; 60 105 } 61 106 62 107 public static function getImageMap() { ··· 64 109 $root = $root.'/resources/builtin/alphanumeric/'; 65 110 66 111 $map = array(); 67 - $list = Filesystem::listDirectory($root, $include_hidden = false); 112 + $list = id(new FileFinder($root)) 113 + ->withType('f') 114 + ->withFollowSymlinks(true) 115 + ->find(); 116 + 68 117 foreach ($list as $file) { 69 - $key = 'alphanumeric/'.$file; 70 - $map[$key] = $root.$file; 118 + $map['alphanumeric/'.$file] = $root.$file; 71 119 } 120 + return $map; 121 + } 122 + 123 + public function getUniqueProfileImage($username) { 124 + $pack_map = $this->getImagePackMap(); 125 + $image_map = $this->getImageMap(); 126 + $color_map = $this->getColorMap(); 127 + $border_map = $this->getBorderMap(); 128 + $file = phutil_utf8_strtoupper(substr($username, 0, 1)); 129 + 130 + $pack_count = count($pack_map); 131 + $color_count = count($color_map); 132 + $border_count = count($border_map); 133 + 134 + $pack_seed = $username.'_pack'; 135 + $color_seed = $username.'_color'; 136 + $border_seed = $username.'_border'; 137 + 138 + $pack_key = 139 + PhabricatorHash::digestToRange($pack_seed, 1, $pack_count); 140 + $color_key = 141 + PhabricatorHash::digestToRange($color_seed, 1, $color_count); 142 + $border_key = 143 + PhabricatorHash::digestToRange($border_seed, 1, $border_count); 144 + 145 + $pack = $pack_map[$pack_key]; 146 + $icon = 'alphanumeric/'.$pack.'/'.$file.'.png'; 147 + $color = $color_map[$color_key]; 148 + $border = $border_map[$border_key]; 149 + 150 + if (!isset($image_map[$icon])) { 151 + $icon = 'alphanumeric/'.$pack.'/_default.png'; 152 + } 153 + 154 + return array('color' => $color, 'icon' => $icon, 'border' => $border); 155 + } 156 + 157 + public function getUserProfileImageFile($username) { 158 + $unique = $this->getUniqueProfileImage($username); 159 + 160 + $composer = id(new self()) 161 + ->setIcon($unique['icon']) 162 + ->setColor($unique['color']) 163 + ->setBorder($unique['border']); 164 + 165 + $data = $composer->loadBuiltinFileData(); 166 + 167 + $unguarded = AphrontWriteGuard::beginScopedUnguardedWrites(); 168 + $file = PhabricatorFile::newFromFileData( 169 + $data, 170 + array( 171 + 'name' => $composer->getBuiltinDisplayName(), 172 + 'profile' => true, 173 + 'canCDN' => true, 174 + )); 175 + unset($unguarded); 176 + 177 + return $file; 178 + } 179 + 180 + public static function getImagePackMap() { 181 + $root = dirname(phutil_get_library_root('phabricator')); 182 + $root = $root.'/resources/builtin/alphanumeric/'; 183 + 184 + $map = id(new FileFinder($root)) 185 + ->withType('d') 186 + ->withFollowSymlinks(false) 187 + ->find(); 188 + 189 + return $map; 190 + } 191 + 192 + public static function getBorderMap() { 193 + 194 + $map = array( 195 + array(0, 0, 0, 0), 196 + array(0, 0, 0, 0.3), 197 + array(255, 255, 255, 0.4), 198 + array(255, 255, 255, 0.7), 199 + ); 72 200 73 201 return $map; 74 202 } 75 203 76 204 public static function getColorMap() { 205 + // 206 + // Generated Colors 207 + // http://tools.medialab.sciences-po.fr/iwanthue/ 208 + // 77 209 $map = array( 78 210 '#335862', 79 - '#dfc47b', 80 211 '#2d5192', 81 - '#c0bc6e', 82 212 '#3c5da0', 83 213 '#99cd86', 84 214 '#704889', ··· 91 221 '#4bd0e3', 92 222 '#a25542', 93 223 '#4eb4f3', 94 - '#705412', 95 224 '#6da8ec', 96 225 '#545608', 97 226 '#829ce5', 98 227 '#68681d', 99 228 '#607bc2', 100 - '#d1b66e', 101 229 '#4b69ad', 102 - '#a4a154', 103 230 '#236ead', 104 - '#daa969', 105 231 '#31a0de', 106 - '#996f31', 107 232 '#4f8ed0', 108 233 '#846f2a', 109 234 '#bdb0f0', ··· 121 246 '#7f4c7f', 122 247 '#a1bb7a', 123 248 '#65558f', 124 - '#c2a962', 125 249 '#445082', 126 250 '#c9ca8e', 127 251 '#265582', 128 252 '#f4b189', 129 253 '#265582', 130 - '#bd8f50', 131 254 '#40b8e1', 132 255 '#814a28', 133 256 '#80c8f6', ··· 142 265 '#b888c9', 143 266 '#476025', 144 267 '#9987c5', 145 - '#828136', 146 268 '#7867a3', 147 269 '#769b5a', 148 270 '#c46e9d', ··· 161 283 '#45a998', 162 284 '#faa38c', 163 285 '#265582', 164 - '#ad954f', 165 286 '#265582', 166 287 '#e4b788', 167 288 '#265582', ··· 187 308 '#ae78ad', 188 309 '#569160', 189 310 '#d898be', 190 - '#525620', 191 311 '#8eb4e8', 192 312 '#5e622c', 193 313 '#929ad3', ··· 209 329 '#63acda', 210 330 '#7b5d30', 211 331 '#66bed6', 212 - '#a66c4e', 213 332 '#3585b0', 214 - '#ba865c', 215 333 '#5880b0', 216 - '#9b864d', 217 334 '#739acc', 218 - '#9d764a', 219 335 '#48a3ba', 220 336 '#9d565b', 221 337 '#7fc4ca', ··· 232 348 '#6bafb6', 233 349 '#8c5744', 234 350 '#84b9d6', 235 - '#725238', 236 351 '#9db3d6', 237 - '#816f3e', 238 352 '#777cad', 239 - '#a6a86e', 240 353 '#826693', 241 354 '#86a779', 242 355 '#9d7fad', 243 - '#8b8e55', 244 356 '#b193c2', 245 357 '#547348', 246 358 '#d5adcb', ··· 250 362 '#b2add6', 251 363 '#5a623d', 252 364 '#9793bb', 253 - '#bea975', 254 365 '#3c5472', 255 366 '#d5c5a1', 256 367 '#5e5a7f', 257 - '#b09c68', 258 368 '#2c647e', 259 369 '#d8b194', 260 370 '#49607f', ··· 269 379 '#ad697e', 270 380 '#799a6d', 271 381 '#916b88', 272 - '#aeb68d', 273 382 '#69536b', 274 383 '#b4c4ad', 275 384 '#845865', ··· 291 400 '#50959b', 292 401 '#b27d7a', 293 402 '#335862', 294 - '#b2a381', 295 403 '#335862', 296 404 '#bcadc4', 297 405 '#706343', 298 406 '#749ebc', 299 407 '#8c6a50', 300 408 '#92b8c4', 301 - '#a07d62', 302 409 '#758cad', 303 410 '#868e67', 304 411 '#335862', 305 - '#b6978c', 306 412 '#335862', 307 - '#9e8f6e', 308 413 '#335862', 309 414 '#ac7e8b', 310 415 '#77a185', ··· 314 419 '#467a70', 315 420 '#9b7d73', 316 421 '#335862', 317 - '#8a7c5b', 318 422 '#335862', 319 423 '#8c9c85', 320 424 '#335862', ··· 331 435 '#335862', 332 436 '#335862', 333 437 '#335862', 334 - ); 335 - return $map; 336 - } 337 - 338 - public static function getBorderMap() { 339 - $map = array( 340 - 'rgba(0,0,0,.3);', // Darker 341 - 'rgba(255,255,255,.5);', // Lighter 342 438 ); 343 439 return $map; 344 440 }
+38 -3
src/applications/people/controller/PhabricatorPeopleProfilePictureController.php
··· 30 30 $e_file = true; 31 31 $errors = array(); 32 32 33 + // Verify install has GD extension, otherwise default to avatar.png 34 + $gd = function_exists('imagecreatefromstring'); 35 + 33 36 if ($request->isFormPost()) { 34 37 $phid = $request->getStr('phid'); 35 38 $is_default = false; 36 39 if ($phid == PhabricatorPHIDConstants::PHID_VOID) { 37 - $phid = null; 38 - $is_default = true; 40 + // Compose the builtin unique image 41 + if ($gd) { 42 + $file = id(new PhabricatorFilesComposeAvatarBuiltinFile()) 43 + ->getUserProfileImageFile($name); 44 + } else { 45 + $phid = null; 46 + $is_default = true; 47 + } 48 + 39 49 } else if ($phid) { 40 50 $file = id(new PhabricatorFileQuery()) 41 51 ->setViewer($viewer) ··· 86 96 $form = id(new PHUIFormLayoutView()) 87 97 ->setUser($viewer); 88 98 89 - $default_image = PhabricatorFile::loadBuiltin($viewer, 'profile.png'); 99 + if ($gd) { 100 + $unique_default = id(new PhabricatorFilesComposeAvatarBuiltinFile()) 101 + ->getUniqueProfileImage($name); 102 + $default_image = PhabricatorFile::loadBuiltin( 103 + $viewer, $unique_default['icon']); 104 + } else { 105 + $unique_default = null; 106 + $default_image = PhabricatorFile::loadBuiltin($viewer, 'profile.png'); 107 + } 90 108 91 109 $images = array(); 92 110 ··· 163 181 } 164 182 } 165 183 184 + $default_style = array(); 185 + if ($unique_default) { 186 + $border_color = implode(', ', $unique_default['border']); 187 + $default_style = array( 188 + 'background-color: '.$unique_default['color'].';', 189 + 'border: 4px solid rgba('.$border_color.');', 190 + 'height: 42px;', 191 + 'width: 42px', 192 + ); 193 + } 194 + 166 195 $images[PhabricatorPHIDConstants::PHID_VOID] = array( 167 196 'uri' => $default_image->getBestURI(), 168 197 'tip' => pht('Default Picture'), 198 + 'style' => implode(' ', $default_style), 169 199 ); 170 200 171 201 require_celerity_resource('people-profile-css'); ··· 173 203 174 204 $buttons = array(); 175 205 foreach ($images as $phid => $spec) { 206 + $style = null; 207 + if (isset($spec['style'])) { 208 + $style = $spec['style']; 209 + } 176 210 $button = javelin_tag( 177 211 'button', 178 212 array( ··· 189 223 'height' => 50, 190 224 'width' => 50, 191 225 'src' => $spec['uri'], 226 + 'style' => $style, 192 227 ))); 193 228 194 229 $button = array(
+20 -6
src/applications/uiexample/examples/PhabricatorFilesComposeAvatarExample.php
··· 15 15 $viewer = $request->getUser(); 16 16 17 17 $colors = PhabricatorFilesComposeAvatarBuiltinFile::getColorMap(); 18 + $packs = PhabricatorFilesComposeAvatarBuiltinFile::getImagePackMap(); 18 19 $builtins = PhabricatorFilesComposeAvatarBuiltinFile::getImageMap(); 19 20 $borders = PhabricatorFilesComposeAvatarBuiltinFile::getBorderMap(); 20 21 21 - shuffle($colors); 22 22 $images = array(); 23 23 foreach ($builtins as $builtin => $raw_file) { 24 24 $file = PhabricatorFile::loadBuiltin($viewer, $builtin); ··· 26 26 } 27 27 28 28 $content = array(); 29 + shuffle($colors); 29 30 foreach ($colors as $color) { 30 31 shuffle($borders); 32 + $color_const = hexdec(trim($color, '#')); 31 33 $border = head($borders); 34 + $border_color = implode(', ', $border); 32 35 33 36 $styles = array(); 34 37 $styles[] = 'background-color: '.$color.';'; 35 38 $styles[] = 'display: inline-block;'; 36 - $styles[] = 'height: 46px;'; 37 - $styles[] = 'width: 46px;'; 39 + $styles[] = 'height: 42px;'; 40 + $styles[] = 'width: 42px;'; 38 41 $styles[] = 'border-radius: 3px;'; 39 - $styles[] = 'border: 4px solid '.$border.';'; 42 + $styles[] = 'border: 4px solid rgba('.$border_color.');'; 40 43 41 44 shuffle($images); 42 45 $png = head($images); ··· 45 48 'img', 46 49 array( 47 50 'src' => $png, 48 - 'height' => 46, 49 - 'width' => 46, 51 + 'height' => 42, 52 + 'width' => 42, 50 53 )); 51 54 52 55 $tag = phutil_tag( ··· 65 68 $tag); 66 69 } 67 70 71 + $count = new PhutilNumber( 72 + count($colors) * count($builtins) * count($borders)); 73 + 74 + $infoview = id(new PHUIInfoView()) 75 + ->setSeverity(PHUIInfoView::SEVERITY_NOTICE) 76 + ->appendChild(pht('This installation can generate %s unique '. 77 + 'avatars. You can add additional image packs in '. 78 + 'resources/builtins/alphanumeric/.', $count)); 79 + 80 + $info = phutil_tag_div('pmb', $infoview); 68 81 $view = phutil_tag_div('ml', $content); 69 82 70 83 return phutil_tag( 71 84 'div', 72 85 array(), 73 86 array( 87 + $info, 74 88 $view, 75 89 )); 76 90 }
+15
src/infrastructure/util/PhabricatorHash.php
··· 78 78 return $result; 79 79 } 80 80 81 + public static function digestToRange($string, $min, $max) { 82 + if ($min > $max) { 83 + throw new Exception(pht('Maximum must be larger than minimum.')); 84 + } 85 + 86 + if ($min == $max) { 87 + return $min; 88 + } 89 + 90 + $hash = sha1($string, $raw_output = true); 91 + $value = head(unpack('L', $hash)); 92 + 93 + return $min + ($value % ($max - $min)); 94 + } 95 + 81 96 82 97 /** 83 98 * Shorten a string to a maximum byte length in a collision-resistant way