diff --git a/webroot/rsrc/favicons/apple-touch-icon-120x120.png b/resources/builtin/favicon/default-120x120.png rename from webroot/rsrc/favicons/apple-touch-icon-120x120.png rename to resources/builtin/favicon/default-120x120.png diff --git a/webroot/rsrc/favicons/favicon-128.png b/resources/builtin/favicon/default-128x128.png rename from webroot/rsrc/favicons/favicon-128.png rename to resources/builtin/favicon/default-128x128.png diff --git a/webroot/rsrc/favicons/apple-touch-icon-152x152.png b/resources/builtin/favicon/default-152x152.png rename from webroot/rsrc/favicons/apple-touch-icon-152x152.png rename to resources/builtin/favicon/default-152x152.png diff --git a/webroot/rsrc/favicons/apple-touch-icon-76x76.png b/resources/builtin/favicon/default-76x76.png rename from webroot/rsrc/favicons/apple-touch-icon-76x76.png rename to resources/builtin/favicon/default-76x76.png diff --git a/resources/builtin/favicon/dot-pink-64x64.png b/resources/builtin/favicon/dot-pink-64x64.png new file mode 100644 index 0000000000000000000000000000000000000000..0000000000000000000000000000000000000000 GIT binary patch literal 0 Hc$@ 'f6d809c0', 'diffusion.pkg.css' => 'a2d17c7d', 'diffusion.pkg.js' => '6134c5a1', - 'favicon.ico' => '30672e08', 'maniphest.pkg.css' => '4845691a', 'maniphest.pkg.js' => '4d7e79c8', 'rsrc/audio/basic/alert.mp3' => '98461568', @@ -270,28 +269,8 @@ 'rsrc/externals/javelin/lib/control/typeahead/source/TypeaheadPreloadedSource.js' => '54f314a0', 'rsrc/externals/javelin/lib/control/typeahead/source/TypeaheadSource.js' => 'ab9e0a82', 'rsrc/externals/javelin/lib/control/typeahead/source/TypeaheadStaticSource.js' => '6c0e62fa', - 'rsrc/favicons/apple-touch-icon-114x114.png' => '12a24178', - 'rsrc/favicons/apple-touch-icon-120x120.png' => '0d1543c7', - 'rsrc/favicons/apple-touch-icon-144x144.png' => '8043b5a5', - 'rsrc/favicons/apple-touch-icon-152x152.png' => '65905ecd', - 'rsrc/favicons/apple-touch-icon-57x57.png' => '2bfc7b0a', - 'rsrc/favicons/apple-touch-icon-60x60.png' => '8ff52925', - 'rsrc/favicons/apple-touch-icon-72x72.png' => 'a2bb65d6', - 'rsrc/favicons/apple-touch-icon-76x76.png' => '2d061a11', - 'rsrc/favicons/favicon-128.png' => '72f7e812', 'rsrc/favicons/favicon-16x16.png' => 'fc6275ba', - 'rsrc/favicons/favicon-196x196.png' => '95db275e', - 'rsrc/favicons/favicon-32x32.png' => '5bd18b6c', - 'rsrc/favicons/favicon-96x96.png' => '7242c8e9', - 'rsrc/favicons/favicon-mention.ico' => '1fdd0fa4', - 'rsrc/favicons/favicon-message.ico' => '115bc010', - 'rsrc/favicons/favicon.ico' => 'cdb11121', 'rsrc/favicons/mask-icon.svg' => 'e132a80f', - 'rsrc/favicons/mstile-144x144.png' => '310c2ee5', - 'rsrc/favicons/mstile-150x150.png' => '74bf5133', - 'rsrc/favicons/mstile-310x150.png' => '4a49d3ee', - 'rsrc/favicons/mstile-310x310.png' => 'a52ab264', - 'rsrc/favicons/mstile-70x70.png' => '5edce7b8', 'rsrc/image/BFCFDA.png' => 'd5ec91f4', 'rsrc/image/actions/edit.png' => '2fc41442', 'rsrc/image/avatar.png' => '17d346a4', diff --git a/src/__phutil_library_map__.php b/src/__phutil_library_map__.php --- a/src/__phutil_library_map__.php +++ b/src/__phutil_library_map__.php @@ -2953,6 +2953,8 @@ 'PhabricatorFactObjectDimension' => 'applications/fact/storage/PhabricatorFactObjectDimension.php', 'PhabricatorFactRaw' => 'applications/fact/storage/PhabricatorFactRaw.php', 'PhabricatorFactUpdateIterator' => 'applications/fact/extract/PhabricatorFactUpdateIterator.php', + 'PhabricatorFaviconRef' => 'applications/files/favicon/PhabricatorFaviconRef.php', + 'PhabricatorFaviconRefQuery' => 'applications/files/favicon/PhabricatorFaviconRefQuery.php', 'PhabricatorFavoritesApplication' => 'applications/favorites/application/PhabricatorFavoritesApplication.php', 'PhabricatorFavoritesController' => 'applications/favorites/controller/PhabricatorFavoritesController.php', 'PhabricatorFavoritesMainMenuBarExtension' => 'applications/favorites/engineextension/PhabricatorFavoritesMainMenuBarExtension.php', @@ -4331,7 +4333,6 @@ 'PhabricatorSystemDAO' => 'applications/system/storage/PhabricatorSystemDAO.php', 'PhabricatorSystemDestructionGarbageCollector' => 'applications/system/garbagecollector/PhabricatorSystemDestructionGarbageCollector.php', 'PhabricatorSystemDestructionLog' => 'applications/system/storage/PhabricatorSystemDestructionLog.php', - 'PhabricatorSystemFaviconController' => 'applications/system/controller/PhabricatorSystemFaviconController.php', 'PhabricatorSystemReadOnlyController' => 'applications/system/controller/PhabricatorSystemReadOnlyController.php', 'PhabricatorSystemRemoveDestroyWorkflow' => 'applications/system/management/PhabricatorSystemRemoveDestroyWorkflow.php', 'PhabricatorSystemRemoveLogWorkflow' => 'applications/system/management/PhabricatorSystemRemoveLogWorkflow.php', @@ -8512,6 +8513,8 @@ 'PhabricatorFactObjectDimension' => 'PhabricatorFactDimension', 'PhabricatorFactRaw' => 'PhabricatorFactDAO', 'PhabricatorFactUpdateIterator' => 'PhutilBufferedIterator', + 'PhabricatorFaviconRef' => 'Phobject', + 'PhabricatorFaviconRefQuery' => 'Phobject', 'PhabricatorFavoritesApplication' => 'PhabricatorApplication', 'PhabricatorFavoritesController' => 'PhabricatorController', 'PhabricatorFavoritesMainMenuBarExtension' => 'PhabricatorMainMenuBarExtension', @@ -10142,7 +10145,6 @@ 'PhabricatorSystemDAO' => 'PhabricatorLiskDAO', 'PhabricatorSystemDestructionGarbageCollector' => 'PhabricatorGarbageCollector', 'PhabricatorSystemDestructionLog' => 'PhabricatorSystemDAO', - 'PhabricatorSystemFaviconController' => 'PhabricatorController', 'PhabricatorSystemReadOnlyController' => 'PhabricatorController', 'PhabricatorSystemRemoveDestroyWorkflow' => 'PhabricatorSystemRemoveWorkflow', 'PhabricatorSystemRemoveLogWorkflow' => 'PhabricatorSystemRemoveWorkflow', diff --git a/src/applications/config/option/PhabricatorUIConfigOptions.php b/src/applications/config/option/PhabricatorUIConfigOptions.php --- a/src/applications/config/option/PhabricatorUIConfigOptions.php +++ b/src/applications/config/option/PhabricatorUIConfigOptions.php @@ -64,6 +64,10 @@ "Phabricator logo in the site header.\n\n". " - **Wordmark**: Choose new text to display next to the logo. ". "By default, the header displays //Phabricator//.\n\n")), + $this->newOption('ui.favicons', 'wild', array()) + ->setSummary(pht('Customize favicons.')) + ->setDescription(pht('Customize favicons.')) + ->setLocked(true), $this->newOption('ui.footer-items', $footer_type, array()) ->setSummary( pht( diff --git a/src/applications/files/favicon/PhabricatorFaviconRef.php b/src/applications/files/favicon/PhabricatorFaviconRef.php new file mode 100644 --- /dev/null +++ b/src/applications/files/favicon/PhabricatorFaviconRef.php @@ -0,0 +1,447 @@ +emblems = array(null, null, null, null); + } + + public function setViewer(PhabricatorUser $viewer) { + $this->viewer = $viewer; + return $this; + } + + public function getViewer() { + return $this->viewer; + } + + public function setWidth($width) { + $this->width = $width; + return $this; + } + + public function getWidth() { + return $this->width; + } + + public function setHeight($height) { + $this->height = $height; + return $this; + } + + public function getHeight() { + return $this->height; + } + + public function setEmblems(array $emblems) { + if (count($emblems) !== 4) { + throw new Exception( + pht( + 'Expected four elements in icon emblem list. To omit an emblem, '. + 'pass "null".')); + } + + $this->emblems = $emblems; + return $this; + } + + public function getEmblems() { + return $this->emblems; + } + + public function setURI($uri) { + $this->uri = $uri; + return $this; + } + + public function getURI() { + return $this->uri; + } + + public function setCacheKey($cache_key) { + $this->cacheKey = $cache_key; + return $this; + } + + public function getCacheKey() { + return $this->cacheKey; + } + + public function newDigest() { + return PhabricatorHash::digestForIndex(serialize($this->toDictionary())); + } + + public function toDictionary() { + return array( + 'width' => $this->width, + 'height' => $this->height, + 'emblems' => $this->emblems, + ); + } + + public static function newConfigurationDigest() { + $all_resources = self::getAllResources(); + + // Because we need to access this cache on every page, it's very sticky. + // Try to dirty it automatically if any relevant configuration changes. + $inputs = array( + 'resources' => $all_resources, + 'prod' => PhabricatorEnv::getProductionURI('/'), + 'cdn' => PhabricatorEnv::getEnvConfig('security.alternate-file-domain'), + 'havepng' => function_exists('imagepng'), + ); + + return PhabricatorHash::digestForIndex(serialize($inputs)); + } + + private static function getAllResources() { + $custom_resources = PhabricatorEnv::getEnvConfig('ui.favicons'); + + foreach ($custom_resources as $key => $custom_resource) { + $custom_resources[$key] = array( + 'source-type' => 'file', + 'default' => false, + ) + $custom_resource; + } + + $builtin_resources = self::getBuiltinResources(); + + return array_merge($builtin_resources, $custom_resources); + } + + private static function getBuiltinResources() { + return array( + array( + 'source-type' => 'builtin', + 'source' => 'favicon/default-76x76.png', + 'version' => 1, + 'width' => 76, + 'height' => 76, + 'default' => true, + ), + array( + 'source-type' => 'builtin', + 'source' => 'favicon/default-120x120.png', + 'version' => 1, + 'width' => 120, + 'height' => 120, + 'default' => true, + ), + array( + 'source-type' => 'builtin', + 'source' => 'favicon/default-128x128.png', + 'version' => 1, + 'width' => 128, + 'height' => 128, + 'default' => true, + ), + array( + 'source-type' => 'builtin', + 'source' => 'favicon/default-152x152.png', + 'version' => 1, + 'width' => 152, + 'height' => 152, + 'default' => true, + ), + array( + 'source-type' => 'builtin', + 'source' => 'favicon/dot-pink-64x64.png', + 'version' => 1, + 'width' => 64, + 'height' => 64, + 'emblem' => 'dot-pink', + 'default' => true, + ), + array( + 'source-type' => 'builtin', + 'source' => 'favicon/dot-red-64x64.png', + 'version' => 1, + 'width' => 64, + 'height' => 64, + 'emblem' => 'dot-red', + 'default' => true, + ), + ); + } + + public function newURI() { + $dst_w = $this->getWidth(); + $dst_h = $this->getHeight(); + + $template = $this->newTemplateFile(null, $dst_w, $dst_h); + $template_file = $template['file']; + + $cache = $this->loadCachedFile($template_file); + if ($cache) { + return $cache->getViewURI(); + } + + $data = $this->newCompositedFavicon($template); + + $unguarded = AphrontWriteGuard::beginScopedUnguardedWrites(); + + $caught = null; + try { + $favicon_file = $this->newFaviconFile($data); + + $xform = id(new PhabricatorTransformedFile()) + ->setOriginalPHID($template_file->getPHID()) + ->setTransformedPHID($favicon_file->getPHID()) + ->setTransform($this->getCacheKey()); + + try { + $xform->save(); + } catch (AphrontDuplicateKeyQueryException $ex) { + unset($unguarded); + + $cache = $this->loadCachedFile($template_file); + if (!$cache) { + throw $ex; + } + + id(new PhabricatorDestructionEngine()) + ->destroyObject($favicon_file); + + return $cache->getViewURI(); + } + } catch (Exception $ex) { + $caught = $ex; + } + + unset($unguarded); + + if ($caught) { + throw $caught; + } + + return $favicon_file->getViewURI(); + } + + private function loadCachedFile(PhabricatorFile $template_file) { + $viewer = $this->getViewer(); + + $xform = id(new PhabricatorTransformedFile())->loadOneWhere( + 'originalPHID = %s AND transform = %s', + $template_file->getPHID(), + $this->getCacheKey()); + if (!$xform) { + return null; + } + + return id(new PhabricatorFileQuery()) + ->setViewer($viewer) + ->withPHIDs(array($xform->getTransformedPHID())) + ->executeOne(); + } + + private function newCompositedFavicon($template) { + $dst_w = $this->getWidth(); + $dst_h = $this->getHeight(); + $src_w = $template['width']; + $src_h = $template['height']; + + $template_data = $template['file']->loadFileData(); + + if (!function_exists('imagecreatefromstring')) { + return $template_data; + } + + $src = @imagecreatefromstring($template_data); + if (!$src) { + return $template_data; + } + + $dst = imagecreatetruecolor($dst_w, $dst_h); + imagesavealpha($dst, true); + + $transparent = imagecolorallocatealpha($dst, 0, 255, 0, 127); + imagefill($dst, 0, 0, $transparent); + + imagecopyresampled( + $dst, + $src, + 0, + 0, + 0, + 0, + $dst_w, + $dst_h, + $src_w, + $src_h); + + // Now, copy any icon emblems on top of the image. These are dots or other + // marks used to indicate status information. + $emblem_w = (int)floor(min($dst_w, $dst_h) / 2); + $emblem_h = $emblem_w; + foreach ($this->emblems as $key => $emblem) { + if ($emblem === null) { + continue; + } + + $emblem_template = $this->newTemplateFile( + $emblem, + $emblem_w, + $emblem_h); + + switch ($key) { + case 0: + $emblem_x = $dst_w - $emblem_w; + $emblem_y = 0; + break; + case 1: + $emblem_x = $dst_w - $emblem_w; + $emblem_y = $dst_h - $emblem_h; + break; + case 2: + $emblem_x = 0; + $emblem_y = $dst_h - $emblem_h; + break; + case 3: + $emblem_x = 0; + $emblem_y = 0; + break; + } + + $emblem_data = $emblem_template['file']->loadFileData(); + + $src = @imagecreatefromstring($emblem_data); + if (!$src) { + continue; + } + + imagecopyresampled( + $dst, + $src, + $emblem_x, + $emblem_y, + 0, + 0, + $emblem_w, + $emblem_h, + $emblem_template['width'], + $emblem_template['height']); + } + + return PhabricatorImageTransformer::saveImageDataInAnyFormat( + $dst, + 'image/png'); + } + + private function newTemplateFile($emblem, $width, $height) { + $all_resources = self::getAllResources(); + + $scores = array(); + $ratio = $width / $height; + foreach ($all_resources as $key => $resource) { + // We can't use an emblem resource for a different emblem, nor for an + // icon base. We also can't use an icon base as an emblem. That is, if + // we're looking for a picture of a red dot, we have to actually find + // a red dot, not just any image which happens to have a similar size. + if (idx($resource, 'emblem') !== $emblem) { + continue; + } + + $resource_width = $resource['width']; + $resource_height = $resource['height']; + + // Never use a resource with a different aspect ratio. + if (($resource_width / $resource_height) !== $ratio) { + continue; + } + + // Try to use custom resources instead of default resources. + if ($resource['default']) { + $default_score = 1; + } else { + $default_score = 0; + } + + $width_diff = ($resource_width - $width); + + // If we have to resize an image, we'd rather scale a larger image down + // than scale a smaller image up. + if ($width_diff < 0) { + $scale_score = 1; + } else { + $scale_score = 0; + } + + // Otherwise, we'd rather scale an image a little bit (ideally, zero) + // than scale an image a lot. + $width_score = abs($width_diff); + + $scores[$key] = id(new PhutilSortVector()) + ->addInt($default_score) + ->addInt($scale_score) + ->addInt($width_score); + } + + if (!$scores) { + if ($emblem === null) { + throw new Exception( + pht( + 'Found no background template resource for dimensions %dx%d.', + $width, + $height)); + } else { + throw new Exception( + pht( + 'Found no template resource (for emblem "%s") with dimensions '. + '%dx%d.', + $emblem, + $width, + $height)); + } + } + + $scores = msortv($scores, 'getSelf'); + $best_score = head_key($scores); + + $viewer = $this->getViewer(); + + $resource = $all_resources[$best_score]; + if ($resource['source-type'] === 'builtin') { + $file = PhabricatorFile::loadBuiltin($viewer, $resource['source']); + if (!$file) { + throw new Exception( + pht( + 'Failed to load favicon template builtin "%s".', + $resource['source'])); + } + } else { + $file = id(new PhabricatorFileQuery()) + ->setViewer($viewer) + ->withPHIDs(array($resource['source'])) + ->executeOne(); + if (!$file) { + throw new Exception( + pht( + 'Failed to load favicon template with PHID "%s".', + $resource['source'])); + } + } + + return array( + 'width' => $resource['width'], + 'height' => $resource['height'], + 'file' => $file, + ); + } + + private function newFaviconFile($data) { + return PhabricatorFile::newFromFileData( + $data, + array( + 'name' => 'favicon', + 'canCDN' => true, + )); + } + +} diff --git a/src/applications/files/favicon/PhabricatorFaviconRefQuery.php b/src/applications/files/favicon/PhabricatorFaviconRefQuery.php new file mode 100644 --- /dev/null +++ b/src/applications/files/favicon/PhabricatorFaviconRefQuery.php @@ -0,0 +1,55 @@ +refs = $refs; + return $this; + } + + public function execute() { + $viewer = PhabricatorUser::getOmnipotentUser(); + + $refs = $this->refs; + + $config_digest = PhabricatorFaviconRef::newConfigurationDigest(); + + $ref_map = array(); + foreach ($refs as $ref) { + $ref_digest = $ref->newDigest(); + $ref_key = "favicon({$config_digest},{$ref_digest},8)"; + + $ref + ->setViewer($viewer) + ->setCacheKey($ref_key); + + $ref_map[$ref_key] = $ref; + } + + $cache = PhabricatorCaches::getImmutableCache(); + $ref_hits = $cache->getKeys(array_keys($ref_map)); + + foreach ($ref_hits as $ref_key => $ref_uri) { + $ref_map[$ref_key]->setURI($ref_uri); + unset($ref_map[$ref_key]); + } + + if ($ref_map) { + $new_map = array(); + foreach ($ref_map as $ref_key => $ref) { + $ref_uri = $ref->newURI(); + $ref->setURI($ref_uri); + $new_map[$ref_key] = $ref_uri; + } + + $cache->setKeys($new_map); + } + + return $refs; + } + + +} diff --git a/src/applications/files/storage/PhabricatorFile.php b/src/applications/files/storage/PhabricatorFile.php --- a/src/applications/files/storage/PhabricatorFile.php +++ b/src/applications/files/storage/PhabricatorFile.php @@ -1152,7 +1152,6 @@ $params = array( 'name' => $builtin->getBuiltinDisplayName(), - 'ttl.relative' => phutil_units('7 days in seconds'), 'canCDN' => true, 'builtin' => $key, ); @@ -1648,7 +1647,7 @@ public function getFieldValuesForConduit() { return array( 'name' => $this->getName(), - 'dataURI' => $this->getCDNURI(), + 'dataURI' => $this->getCDNURI('data'), 'size' => (int)$this->getByteSize(), ); } diff --git a/src/applications/system/application/PhabricatorSystemApplication.php b/src/applications/system/application/PhabricatorSystemApplication.php --- a/src/applications/system/application/PhabricatorSystemApplication.php +++ b/src/applications/system/application/PhabricatorSystemApplication.php @@ -26,7 +26,6 @@ '/readonly/' => array( '(?P[^/]+)/' => 'PhabricatorSystemReadOnlyController', ), - '/favicon.ico' => 'PhabricatorSystemFaviconController', ); } diff --git a/src/applications/system/controller/PhabricatorSystemFaviconController.php b/src/applications/system/controller/PhabricatorSystemFaviconController.php deleted file mode 100644 --- a/src/applications/system/controller/PhabricatorSystemFaviconController.php +++ /dev/null @@ -1,19 +0,0 @@ -setContent($content) - ->setMimeType('image/x-icon') - ->setCacheDurationInSeconds(phutil_units('24 hours in seconds')) - ->setCanCDN(true); - } -} diff --git a/src/view/page/PhabricatorBarePageView.php b/src/view/page/PhabricatorBarePageView.php --- a/src/view/page/PhabricatorBarePageView.php +++ b/src/view/page/PhabricatorBarePageView.php @@ -71,56 +71,24 @@ )); } - $mask_icon = phutil_tag( - 'link', - array( - 'rel' => 'mask-icon', - 'color' => '#3D4B67', - 'href' => celerity_get_resource_uri( - '/rsrc/favicons/mask-icon.svg'), - )); - - $icon_tag_76 = phutil_tag( - 'link', - array( - 'rel' => 'apple-touch-icon', - 'href' => celerity_get_resource_uri( - '/rsrc/favicons/apple-touch-icon-76x76.png'), - )); - - $icon_tag_120 = phutil_tag( - 'link', + $referrer_tag = phutil_tag( + 'meta', array( - 'rel' => 'apple-touch-icon', - 'sizes' => '120x120', - 'href' => celerity_get_resource_uri( - '/rsrc/favicons/apple-touch-icon-120x120.png'), + 'name' => 'referrer', + 'content' => 'no-referrer', )); - $icon_tag_152 = phutil_tag( - 'link', - array( - 'rel' => 'apple-touch-icon', - 'sizes' => '152x152', - 'href' => celerity_get_resource_uri( - '/rsrc/favicons/apple-touch-icon-152x152.png'), - )); - $favicon_tag = phutil_tag( + $mask_icon = phutil_tag( 'link', array( - 'id' => 'favicon', - 'rel' => 'shortcut icon', + 'rel' => 'mask-icon', + 'color' => '#3D4B67', 'href' => celerity_get_resource_uri( - '/rsrc/favicons/favicon.ico'), + '/rsrc/favicons/mask-icon.svg'), )); - $referrer_tag = phutil_tag( - 'meta', - array( - 'name' => 'referrer', - 'content' => 'no-referrer', - )); + $favicon_links = $this->newFavicons(); $response = CelerityAPI::getStaticResourceResponse(); @@ -136,13 +104,10 @@ } return hsprintf( - '%s%s%s%s%s%s%s%s', + '%s%s%s%s%s', $viewport_tag, $mask_icon, - $icon_tag_76, - $icon_tag_120, - $icon_tag_152, - $favicon_tag, + $favicon_links, $referrer_tag, $response->renderResourcesOfType('css')); } @@ -156,4 +121,61 @@ return $response->renderResourcesOfType('js'); } + private function newFavicons() { + $favicon_refs = array( + array( + 'rel' => 'apple-touch-icon', + 'sizes' => '76x76', + 'width' => 76, + 'height' => 76, + ), + array( + 'rel' => 'apple-touch-icon', + 'sizes' => '120x120', + 'width' => 120, + 'height' => 120, + ), + array( + 'rel' => 'apple-touch-icon', + 'sizes' => '152x152', + 'width' => 152, + 'height' => 152, + ), + array( + 'rel' => 'icon', + 'id' => 'favicon', + 'width' => 64, + 'height' => 64, + ), + ); + + $fetch_refs = array(); + foreach ($favicon_refs as $key => $spec) { + $ref = id(new PhabricatorFaviconRef()) + ->setWidth($spec['width']) + ->setHeight($spec['height']); + + $favicon_refs[$key]['ref'] = $ref; + $fetch_refs[] = $ref; + } + + id(new PhabricatorFaviconRefQuery()) + ->withRefs($fetch_refs) + ->execute(); + + $favicon_links = array(); + foreach ($favicon_refs as $spec) { + $favicon_links[] = phutil_tag( + 'link', + array( + 'rel' => $spec['rel'], + 'sizes' => idx($spec, 'sizes'), + 'id' => idx($spec, 'id'), + 'href' => $spec['ref']->getURI(), + )); + } + + return $favicon_links; + } + } diff --git a/src/view/page/menu/PhabricatorMainMenuView.php b/src/view/page/menu/PhabricatorMainMenuView.php --- a/src/view/page/menu/PhabricatorMainMenuView.php +++ b/src/view/page/menu/PhabricatorMainMenuView.php @@ -23,15 +23,29 @@ return $this->controller; } - private function getFaviconURI($type = null) { - switch ($type) { - case 'message': - return celerity_get_resource_uri('/rsrc/favicons/favicon-message.ico'); - case 'mention': - return celerity_get_resource_uri('/rsrc/favicons/favicon-mention.ico'); - default: - return celerity_get_resource_uri('/rsrc/favicons/favicon.ico'); - } + private static function getFavicons() { + $refs = array(); + + $refs['favicon'] = id(new PhabricatorFaviconRef()) + ->setWidth(64) + ->setHeight(64); + + $refs['message_favicon'] = id(new PhabricatorFaviconRef()) + ->setWidth(64) + ->setHeight(64) + ->setEmblems( + array( + 'dot-pink', + null, + null, + null, + )); + + id(new PhabricatorFaviconRefQuery()) + ->withRefs($refs) + ->execute(); + + return mpull($refs, 'getURI'); } public function render() { @@ -428,10 +442,7 @@ 'countType' => $conpherence_data['countType'], 'countNumber' => $message_count_number, 'unreadClass' => 'message-unread', - 'favicon' => $this->getFaviconURI('default'), - 'message_favicon' => $this->getFaviconURI('message'), - 'mention_favicon' => $this->getFaviconURI('mention'), - )); + ) + self::getFavicons()); $message_notification_dropdown = javelin_tag( 'div', @@ -509,10 +520,7 @@ 'countType' => $notification_data['countType'], 'countNumber' => $count_number, 'unreadClass' => 'alert-unread', - 'favicon' => $this->getFaviconURI('default'), - 'message_favicon' => $this->getFaviconURI('message'), - 'mention_favicon' => $this->getFaviconURI('mention'), - )); + ) + self::getFavicons()); $notification_dropdown = javelin_tag( 'div', @@ -594,10 +602,7 @@ 'countType' => null, 'countNumber' => null, 'unreadClass' => 'setup-unread', - 'favicon' => $this->getFaviconURI('default'), - 'message_favicon' => $this->getFaviconURI('message'), - 'mention_favicon' => $this->getFaviconURI('mention'), - )); + ) + self::getFavicons()); $setup_notification_dropdown = javelin_tag( 'div', diff --git a/webroot/favicon.ico b/webroot/favicon.ico deleted file mode 100644 index 0000000000000000000000000000000000000000..0000000000000000000000000000000000000000 GIT binary patch literal 0 Hc$@