Path: blob/master/src/applications/files/builtin/PhabricatorFilesComposeAvatarBuiltinFile.php
12242 views
<?php12final class PhabricatorFilesComposeAvatarBuiltinFile3extends PhabricatorFilesBuiltinFile {45private $icon;6private $color;7private $border;89private $maps = array();1011const VERSION = 'v1';1213public function updateUser(PhabricatorUser $user) {14$username = $user->getUsername();1516$image_map = $this->getMap('image');17$initial = phutil_utf8_strtoupper(substr($username, 0, 1));18$pack = $this->pickMap('pack', $username);19$icon = "alphanumeric/{$pack}/{$initial}.png";20if (!isset($image_map[$icon])) {21$icon = "alphanumeric/{$pack}/_default.png";22}2324$border = $this->pickMap('border', $username);25$color = $this->pickMap('color', $username);2627$data = $this->composeImage($color, $icon, $border);28$name = $this->getImageDisplayName($color, $icon, $border);2930$unguarded = AphrontWriteGuard::beginScopedUnguardedWrites();3132$file = PhabricatorFile::newFromFileData(33$data,34array(35'name' => $name,36'profile' => true,37'canCDN' => true,38));3940$user41->setDefaultProfileImagePHID($file->getPHID())42->setDefaultProfileImageVersion(self::VERSION)43->saveWithoutIndex();4445unset($unguarded);4647return $file;48}4950private function getMap($map_key) {51if (!isset($this->maps[$map_key])) {52switch ($map_key) {53case 'pack':54$map = $this->newPackMap();55break;56case 'image':57$map = $this->newImageMap();58break;59case 'color':60$map = $this->newColorMap();61break;62case 'border':63$map = $this->newBorderMap();64break;65default:66throw new Exception(pht('Unknown map "%s".', $map_key));67}68$this->maps[$map_key] = $map;69}7071return $this->maps[$map_key];72}7374private function pickMap($map_key, $username) {75$map = $this->getMap($map_key);76$seed = $username.'_'.$map_key;77$key = PhabricatorHash::digestToRange($seed, 0, count($map) - 1);78return $map[$key];79}808182public function setIcon($icon) {83$this->icon = $icon;84return $this;85}8687public function getIcon() {88return $this->icon;89}9091public function setColor($color) {92$this->color = $color;93return $this;94}9596public function getColor() {97return $this->color;98}99100public function setBorder($border) {101$this->border = $border;102return $this;103}104105public function getBorder() {106return $this->border;107}108109public function getBuiltinFileKey() {110$icon = $this->getIcon();111$color = $this->getColor();112$border = implode(',', $this->getBorder());113$desc = "compose(icon={$icon}, color={$color}, border={$border}";114$hash = PhabricatorHash::digestToLength($desc, 40);115return "builtin:{$hash}";116}117118public function getBuiltinDisplayName() {119return $this->getImageDisplayName(120$this->getIcon(),121$this->getColor(),122$this->getBorder());123}124125private function getImageDisplayName($icon, $color, $border) {126$border = implode(',', $border);127return "{$icon}-{$color}-{$border}.png";128}129130public function loadBuiltinFileData() {131return $this->composeImage(132$this->getColor(),133$this->getIcon(),134$this->getBorder());135}136137private function composeImage($color, $image, $border) {138// If we don't have the GD extension installed, just return a static139// default profile image rather than trying to compose a dynamic one.140if (!function_exists('imagecreatefromstring')) {141$root = dirname(phutil_get_library_root('phabricator'));142$default_path = $root.'/resources/builtin/profile.png';143return Filesystem::readFile($default_path);144}145146$color_const = hexdec(trim($color, '#'));147$true_border = self::rgba2gd($border);148$image_map = $this->getMap('image');149$data = Filesystem::readFile($image_map[$image]);150151$img = imagecreatefromstring($data);152153// 4 pixel border at 50x50, 32 pixel border at 400x400154$canvas = imagecreatetruecolor(400, 400);155156$image_fill = imagefill($canvas, 0, 0, $color_const);157if (!$image_fill) {158throw new Exception(159pht('Failed to save builtin avatar image data (imagefill).'));160}161162$border_thickness = imagesetthickness($canvas, 64);163if (!$border_thickness) {164throw new Exception(165pht('Failed to save builtin avatar image data (imagesetthickness).'));166}167168$image_rectangle = imagerectangle($canvas, 0, 0, 400, 400, $true_border);169if (!$image_rectangle) {170throw new Exception(171pht('Failed to save builtin avatar image data (imagerectangle).'));172}173174$image_copy = imagecopy($canvas, $img, 0, 0, 0, 0, 400, 400);175if (!$image_copy) {176throw new Exception(177pht('Failed to save builtin avatar image data (imagecopy).'));178}179180return PhabricatorImageTransformer::saveImageDataInAnyFormat(181$canvas,182'image/png');183}184185private static function rgba2gd(array $rgba) {186$r = (int)$rgba[0];187$g = (int)$rgba[1];188$b = (int)$rgba[2];189$a = (int)$rgba[3];190$a = (1 - $a) * 255;191return ($a << 24) | ($r << 16) | ($g << 8) | $b;192}193194private function newImageMap() {195$root = dirname(phutil_get_library_root('phabricator'));196$root = $root.'/resources/builtin/alphanumeric/';197198$map = array();199$list = id(new FileFinder($root))200->withType('f')201->withFollowSymlinks(true)202->find();203204foreach ($list as $file) {205$map['alphanumeric/'.$file] = $root.$file;206}207208return $map;209}210211private function newPackMap() {212$root = dirname(phutil_get_library_root('phabricator'));213$root = $root.'/resources/builtin/alphanumeric/';214215$map = id(new FileFinder($root))216->withType('d')217->withFollowSymlinks(false)218->find();219$map = array_values($map);220221return $map;222}223224private function newBorderMap() {225return array(226array(0, 0, 0, 0),227array(0, 0, 0, 0.3),228array(255, 255, 255, 0.4),229array(255, 255, 255, 0.7),230);231}232233private function newColorMap() {234// Via: http://tools.medialab.sciences-po.fr/iwanthue/235236return array(237'#335862',238'#2d5192',239'#3c5da0',240'#99cd86',241'#704889',242'#5ac59e',243'#984060',244'#33d4d1',245'#9c4050',246'#20d8fd',247'#944937',248'#4bd0e3',249'#a25542',250'#4eb4f3',251'#6da8ec',252'#545608',253'#829ce5',254'#68681d',255'#607bc2',256'#4b69ad',257'#236ead',258'#31a0de',259'#4f8ed0',260'#846f2a',261'#bdb0f0',262'#518342',263'#9166aa',264'#5e904e',265'#f79dcc',266'#158e6b',267'#e189b7',268'#3ba984',269'#a85582',270'#4cccb7',271'#863d67',272'#84c08c',273'#7f4c7f',274'#a1bb7a',275'#65558f',276'#445082',277'#c9ca8e',278'#265582',279'#f4b189',280'#265582',281'#40b8e1',282'#814a28',283'#80c8f6',284'#cf7b5d',285'#1db5c7',286'#c0606e',287'#299a89',288'#ef8ead',289'#296437',290'#d39edb',291'#507436',292'#b888c9',293'#476025',294'#9987c5',295'#7867a3',296'#769b5a',297'#c46e9d',298'#437d4e',299'#d17492',300'#115e41',301'#ec8794',302'#297153',303'#d67381',304'#57c2c3',305'#bc607f',306'#86ceac',307'#7e3e53',308'#72c8b8',309'#884349',310'#45a998',311'#faa38c',312'#265582',313'#265582',314'#e4b788',315'#265582',316'#bbbc81',317'#265582',318'#ccb781',319'#265582',320'#eb957f',321'#15729c',322'#cf996f',323'#369bc5',324'#b6685d',325'#2da0a1',326'#d38275',327'#217e70',328'#ec9da1',329'#146268',330'#e8aa95',331'#3c6796',332'#8da667',333'#935f93',334'#69a573',335'#ae78ad',336'#569160',337'#d898be',338'#8eb4e8',339'#5e622c',340'#929ad3',341'#6c8548',342'#576196',343'#aed0a0',344'#694e79',345'#9abb8d',346'#8c5175',347'#6bb391',348'#8b4a5f',349'#519878',350'#ae7196',351'#3d8465',352'#e69eb3',353'#48663d',354'#cdaede',355'#71743d',356'#63acda',357'#7b5d30',358'#66bed6',359'#3585b0',360'#5880b0',361'#739acc',362'#48a3ba',363'#9d565b',364'#7fc4ca',365'#99566b',366'#94cabf',367'#7b4b49',368'#b1c8eb',369'#4e5632',370'#ecb2c3',371'#2d6158',372'#cf8287',373'#25889f',374'#b2696d',375'#6bafb6',376'#8c5744',377'#84b9d6',378'#9db3d6',379'#777cad',380'#826693',381'#86a779',382'#9d7fad',383'#b193c2',384'#547348',385'#d5adcb',386'#3f674d',387'#c98398',388'#66865a',389'#b2add6',390'#5a623d',391'#9793bb',392'#3c5472',393'#d5c5a1',394'#5e5a7f',395'#2c647e',396'#d8b194',397'#49607f',398'#c7b794',399'#335862',400'#e3aba7',401'#335862',402'#d9b9ad',403'#335862',404'#c48975',405'#347b81',406'#ad697e',407'#799a6d',408'#916b88',409'#69536b',410'#b4c4ad',411'#845865',412'#96b89d',413'#706d92',414'#9aa27a',415'#5b7292',416'#bc967b',417'#417792',418'#ce9793',419'#335862',420'#c898a5',421'#527a5f',422'#b38ba9',423'#648d72',424'#986b78',425'#79afa4',426'#966461',427'#50959b',428'#b27d7a',429'#335862',430'#335862',431'#bcadc4',432'#706343',433'#749ebc',434'#8c6a50',435'#92b8c4',436'#758cad',437'#868e67',438'#335862',439'#335862',440'#335862',441'#ac7e8b',442'#77a185',443'#807288',444'#636f51',445'#a192a9',446'#467a70',447'#9b7d73',448'#335862',449'#335862',450'#8c9c85',451'#335862',452'#81645a',453'#5f9489',454'#335862',455'#789da8',456'#335862',457'#72826c',458'#335862',459'#5c8596',460'#335862',461'#456a74',462'#335862',463'#335862',464'#335862',465);466}467468}469470471