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 @@ -999,6 +999,8 @@ 'DiffusionServeController' => 'applications/diffusion/controller/DiffusionServeController.php', 'DiffusionSetPasswordSettingsPanel' => 'applications/diffusion/panel/DiffusionSetPasswordSettingsPanel.php', 'DiffusionSetupException' => 'applications/diffusion/exception/DiffusionSetupException.php', + 'DiffusionSourceHyperlinkEngineExtension' => 'applications/diffusion/engineextension/DiffusionSourceHyperlinkEngineExtension.php', + 'DiffusionSourceLinkView' => 'applications/diffusion/view/DiffusionSourceLinkView.php', 'DiffusionSubversionCommandEngine' => 'applications/diffusion/protocol/DiffusionSubversionCommandEngine.php', 'DiffusionSubversionSSHWorkflow' => 'applications/diffusion/ssh/DiffusionSubversionSSHWorkflow.php', 'DiffusionSubversionServeSSHWorkflow' => 'applications/diffusion/ssh/DiffusionSubversionServeSSHWorkflow.php', @@ -4340,6 +4342,7 @@ 'PhabricatorRemarkupDocumentEngine' => 'applications/files/document/PhabricatorRemarkupDocumentEngine.php', 'PhabricatorRemarkupEditField' => 'applications/transactions/editfield/PhabricatorRemarkupEditField.php', 'PhabricatorRemarkupFigletBlockInterpreter' => 'infrastructure/markup/interpreter/PhabricatorRemarkupFigletBlockInterpreter.php', + 'PhabricatorRemarkupHyperlinkEngineExtension' => 'applications/remarkup/engineextension/PhabricatorRemarkupHyperlinkEngineExtension.php', 'PhabricatorRemarkupUIExample' => 'applications/uiexample/examples/PhabricatorRemarkupUIExample.php', 'PhabricatorRepositoriesSetupCheck' => 'applications/config/check/PhabricatorRepositoriesSetupCheck.php', 'PhabricatorRepository' => 'applications/repository/storage/PhabricatorRepository.php', @@ -6681,6 +6684,8 @@ 'DiffusionServeController' => 'DiffusionController', 'DiffusionSetPasswordSettingsPanel' => 'PhabricatorSettingsPanel', 'DiffusionSetupException' => 'Exception', + 'DiffusionSourceHyperlinkEngineExtension' => 'PhabricatorRemarkupHyperlinkEngineExtension', + 'DiffusionSourceLinkView' => 'AphrontView', 'DiffusionSubversionCommandEngine' => 'DiffusionCommandEngine', 'DiffusionSubversionSSHWorkflow' => 'DiffusionSSHWorkflow', 'DiffusionSubversionServeSSHWorkflow' => 'DiffusionSubversionSSHWorkflow', @@ -6786,7 +6791,7 @@ 'DoorkeeperExternalObjectQuery' => 'PhabricatorCursorPagedPolicyAwareQuery', 'DoorkeeperFeedStoryPublisher' => 'Phobject', 'DoorkeeperFeedWorker' => 'FeedPushWorker', - 'DoorkeeperHyperlinkEngineExtension' => 'PhutilRemarkupHyperlinkEngineExtension', + 'DoorkeeperHyperlinkEngineExtension' => 'PhabricatorRemarkupHyperlinkEngineExtension', 'DoorkeeperImportEngine' => 'Phobject', 'DoorkeeperJIRAFeedWorker' => 'DoorkeeperFeedWorker', 'DoorkeeperMissingLinkException' => 'Exception', @@ -10595,6 +10600,7 @@ 'PhabricatorRemarkupDocumentEngine' => 'PhabricatorDocumentEngine', 'PhabricatorRemarkupEditField' => 'PhabricatorEditField', 'PhabricatorRemarkupFigletBlockInterpreter' => 'PhutilRemarkupBlockInterpreter', + 'PhabricatorRemarkupHyperlinkEngineExtension' => 'PhutilRemarkupHyperlinkEngineExtension', 'PhabricatorRemarkupUIExample' => 'PhabricatorUIExample', 'PhabricatorRepositoriesSetupCheck' => 'PhabricatorSetupCheck', 'PhabricatorRepository' => array( @@ -10883,7 +10889,7 @@ 'PhabricatorSecuritySetupCheck' => 'PhabricatorSetupCheck', 'PhabricatorSelectEditField' => 'PhabricatorEditField', 'PhabricatorSelectSetting' => 'PhabricatorSetting', - 'PhabricatorSelfHyperlinkEngineExtension' => 'PhutilRemarkupHyperlinkEngineExtension', + 'PhabricatorSelfHyperlinkEngineExtension' => 'PhabricatorRemarkupHyperlinkEngineExtension', 'PhabricatorSessionsSettingsPanel' => 'PhabricatorSettingsPanel', 'PhabricatorSetConfigType' => 'PhabricatorTextConfigType', 'PhabricatorSetting' => 'Phobject', diff --git a/src/applications/diffusion/engineextension/DiffusionSourceHyperlinkEngineExtension.php b/src/applications/diffusion/engineextension/DiffusionSourceHyperlinkEngineExtension.php new file mode 100644 --- /dev/null +++ b/src/applications/diffusion/engineextension/DiffusionSourceHyperlinkEngineExtension.php @@ -0,0 +1,84 @@ +<?php + +final class DiffusionSourceHyperlinkEngineExtension + extends PhabricatorRemarkupHyperlinkEngineExtension { + + const LINKENGINEKEY = 'diffusion-src'; + + public function processHyperlinks(array $hyperlinks) { + $engine = $this->getEngine(); + $viewer = $engine->getConfig('viewer'); + + if (!$viewer) { + return; + } + + $hyperlinks = $this->getSelfLinks($hyperlinks); + + $links = array(); + foreach ($hyperlinks as $link) { + $uri = $link->getURI(); + $uri = new PhutilURI($uri); + + $path = $uri->getPath(); + + $pattern = + '(^'. + '/(?:diffusion|source)'. + '/(?P<identifier>[^/]+)'. + '/browse'. + '/(?P<blob>.*)'. + '\z)'; + $matches = null; + if (!preg_match($pattern, $path, $matches)) { + continue; + } + + $links[] = array( + 'ref' => $link, + 'identifier' => $matches['identifier'], + 'blob' => $matches['blob'], + ); + } + + if (!$links) { + return; + } + + $identifiers = ipull($links, 'identifier'); + + $query = id(new PhabricatorRepositoryQuery()) + ->setViewer($viewer) + ->withIdentifiers($identifiers); + + $query->execute(); + + $repository_map = $query->getIdentifierMap(); + + foreach ($links as $link) { + $identifier = $link['identifier']; + + $repository = idx($repository_map, $identifier); + if (!$repository) { + continue; + } + + $ref = $link['ref']; + $uri = $ref->getURI(); + + + $tag = id(new DiffusionSourceLinkView()) + ->setViewer($viewer) + ->setRepository($repository) + ->setURI($uri) + ->setBlob($link['blob']); + + if (!$ref->isEmbed()) { + $tag->setText($uri); + } + + $ref->setResult($tag); + } + } + +} diff --git a/src/applications/diffusion/request/DiffusionGitRequest.php b/src/applications/diffusion/request/DiffusionGitRequest.php --- a/src/applications/diffusion/request/DiffusionGitRequest.php +++ b/src/applications/diffusion/request/DiffusionGitRequest.php @@ -2,10 +2,6 @@ final class DiffusionGitRequest extends DiffusionRequest { - public function supportsBranches() { - return true; - } - protected function isStableCommit($symbol) { return preg_match('/^[a-f0-9]{40}\z/', $symbol); } diff --git a/src/applications/diffusion/request/DiffusionMercurialRequest.php b/src/applications/diffusion/request/DiffusionMercurialRequest.php --- a/src/applications/diffusion/request/DiffusionMercurialRequest.php +++ b/src/applications/diffusion/request/DiffusionMercurialRequest.php @@ -2,10 +2,6 @@ final class DiffusionMercurialRequest extends DiffusionRequest { - public function supportsBranches() { - return true; - } - protected function isStableCommit($symbol) { return preg_match('/^[a-f0-9]{40}\z/', $symbol); } diff --git a/src/applications/diffusion/request/DiffusionRequest.php b/src/applications/diffusion/request/DiffusionRequest.php --- a/src/applications/diffusion/request/DiffusionRequest.php +++ b/src/applications/diffusion/request/DiffusionRequest.php @@ -28,7 +28,10 @@ private $branchObject = false; private $refAlternatives; - abstract public function supportsBranches(); + final public function supportsBranches() { + return $this->getRepository()->supportsRefs(); + } + abstract protected function isStableCommit($symbol); protected function didInitialize() { diff --git a/src/applications/diffusion/request/DiffusionSvnRequest.php b/src/applications/diffusion/request/DiffusionSvnRequest.php --- a/src/applications/diffusion/request/DiffusionSvnRequest.php +++ b/src/applications/diffusion/request/DiffusionSvnRequest.php @@ -2,10 +2,6 @@ final class DiffusionSvnRequest extends DiffusionRequest { - public function supportsBranches() { - return false; - } - protected function isStableCommit($symbol) { return preg_match('/^[1-9]\d*\z/', $symbol); } diff --git a/src/applications/diffusion/view/DiffusionSourceLinkView.php b/src/applications/diffusion/view/DiffusionSourceLinkView.php new file mode 100644 --- /dev/null +++ b/src/applications/diffusion/view/DiffusionSourceLinkView.php @@ -0,0 +1,208 @@ +<?php + +final class DiffusionSourceLinkView + extends AphrontView { + + private $repository; + private $text; + private $uri; + private $blob; + private $blobMap; + private $refName; + private $path; + private $line; + private $commit; + + public function setRepository($repository) { + $this->repository = $repository; + $this->blobMap = null; + return $this; + } + + public function getRepository() { + return $this->repository; + } + + public function setText($text) { + $this->text = $text; + return $this; + } + + public function getText() { + return $this->text; + } + + public function setURI($uri) { + $this->uri = $uri; + return $this; + } + + public function getURI() { + return $this->uri; + } + + public function setBlob($blob) { + $this->blob = $blob; + $this->blobMap = null; + return $this; + } + + public function getBlob() { + return $this->blob; + } + + public function setRefName($ref_name) { + $this->refName = $ref_name; + return $this; + } + + public function getRefName() { + return $this->refName; + } + + public function setPath($path) { + $this->path = $path; + return $this; + } + + public function getPath() { + return $this->path; + } + + public function setCommit($commit) { + $this->commit = $commit; + return $this; + } + + public function getCommit() { + return $this->commit; + } + + public function setLine($line) { + $this->line = $line; + return $this; + } + + public function getLine() { + return $this->line; + } + + public function getDisplayPath() { + if ($this->path !== null) { + return $this->path; + } + + return $this->getBlobPath(); + } + + public function getDisplayRefName() { + if ($this->refName !== null) { + return $this->refName; + } + + return $this->getBlobRefName(); + } + + public function getDisplayCommit() { + if ($this->commit !== null) { + return $this->commit; + } + + return $this->getBlobCommit(); + } + + public function getDisplayLine() { + if ($this->line !== null) { + return $this->line; + } + + return $this->getBlobLine(); + } + + private function getBlobPath() { + return idx($this->getBlobMap(), 'path'); + } + + private function getBlobRefName() { + return idx($this->getBlobMap(), 'branch'); + } + + private function getBlobLine() { + return idx($this->getBlobMap(), 'line'); + } + + private function getBlobCommit() { + return idx($this->getBlobMap(), 'commit'); + } + + private function getBlobMap() { + if ($this->blobMap === null) { + $repository = $this->getRepository(); + $blob = $this->blob; + + if ($repository && ($blob !== null)) { + $map = DiffusionRequest::parseRequestBlob( + $blob, + $repository->supportsRefs()); + } else { + $map = array(); + } + + $this->blobMap = $map; + } + + return $this->blobMap; + } + + public function render() { + $repository = $this->getRepository(); + $uri = $this->getURI(); + + $color = 'blue'; + $icon = 'fa-file-text-o'; + + $text = $this->getText(); + if (!strlen($text)) { + $path = $this->getDisplayPath(); + + $line = $this->getDisplayLine(); + if ($line !== null) { + $path = pht('%s:%s', $path, $line); + } + + if ($repository) { + $path = pht('%s %s', $repository->getMonogram(), $path); + } + + if ($repository && $repository->supportsRefs()) { + $default_ref = $repository->getDefaultBranch(); + } else { + $default_ref = null; + } + + $ref_name = $this->getDisplayRefName(); + if ($ref_name === $default_ref) { + $ref_name = null; + } + + $commit = $this->getDisplayCommit(); + if ($ref_name !== null && $commit !== null) { + $text = pht('%s (on %s at %s)', $path, $ref_name, $commit); + } else if ($ref_name !== null) { + $text = pht('%s (on %s)', $path, $ref_name); + } else if ($commit !== null) { + $text = pht('%s (at %s)', $path, $commit); + } else { + $text = $path; + } + } + + return id(new PHUITagView()) + ->setType(PHUITagView::TYPE_SHADE) + ->setColor($color) + ->setIcon($icon) + ->setHref($uri) + ->setName($text); + } + +} diff --git a/src/applications/doorkeeper/engineextension/DoorkeeperHyperlinkEngineExtension.php b/src/applications/doorkeeper/engineextension/DoorkeeperHyperlinkEngineExtension.php --- a/src/applications/doorkeeper/engineextension/DoorkeeperHyperlinkEngineExtension.php +++ b/src/applications/doorkeeper/engineextension/DoorkeeperHyperlinkEngineExtension.php @@ -1,7 +1,7 @@ <?php final class DoorkeeperHyperlinkEngineExtension - extends PhutilRemarkupHyperlinkEngineExtension { + extends PhabricatorRemarkupHyperlinkEngineExtension { const LINKENGINEKEY = 'doorkeeper'; diff --git a/src/applications/meta/engineextension/PhabricatorSelfHyperlinkEngineExtension.php b/src/applications/meta/engineextension/PhabricatorSelfHyperlinkEngineExtension.php --- a/src/applications/meta/engineextension/PhabricatorSelfHyperlinkEngineExtension.php +++ b/src/applications/meta/engineextension/PhabricatorSelfHyperlinkEngineExtension.php @@ -1,7 +1,7 @@ <?php final class PhabricatorSelfHyperlinkEngineExtension - extends PhutilRemarkupHyperlinkEngineExtension { + extends PhabricatorRemarkupHyperlinkEngineExtension { const LINKENGINEKEY = 'phabricator-self'; @@ -15,15 +15,7 @@ return; } - // Find links which point to resources on the Phabricator install itself. - // We're going to try to enhance these. - $self_links = array(); - foreach ($hyperlinks as $link) { - $uri = $link->getURI(); - if (PhabricatorEnv::isSelfURI($uri)) { - $self_links[] = $link; - } - } + $self_links = $this->getSelfLinks($hyperlinks); // For links in the form "/X123", we can reasonably guess that they are // fairly likely to be object names. Try to look them up. diff --git a/src/applications/remarkup/engineextension/PhabricatorRemarkupHyperlinkEngineExtension.php b/src/applications/remarkup/engineextension/PhabricatorRemarkupHyperlinkEngineExtension.php new file mode 100644 --- /dev/null +++ b/src/applications/remarkup/engineextension/PhabricatorRemarkupHyperlinkEngineExtension.php @@ -0,0 +1,32 @@ +<?php + +abstract class PhabricatorRemarkupHyperlinkEngineExtension + extends PhutilRemarkupHyperlinkEngineExtension { + + final protected function getSelfLinks(array $hyperlinks) { + assert_instances_of($hyperlinks, 'PhutilRemarkupHyperlinkRef'); + + $allowed_protocols = array( + 'http' => true, + 'https' => true, + ); + + $results = array(); + foreach ($hyperlinks as $link) { + $uri = $link->getURI(); + + if (!PhabricatorEnv::isSelfURI($uri)) { + continue; + } + + $protocol = id(new PhutilURI($uri))->getProtocol(); + if (!isset($allowed_protocols[$protocol])) { + continue; + } + + $results[] = $link; + } + + return $results; + } +} diff --git a/src/applications/repository/storage/PhabricatorRepository.php b/src/applications/repository/storage/PhabricatorRepository.php --- a/src/applications/repository/storage/PhabricatorRepository.php +++ b/src/applications/repository/storage/PhabricatorRepository.php @@ -2040,6 +2040,15 @@ return true; } + + public function supportsRefs() { + if ($this->isSVN()) { + return false; + } + + return true; + } + public function getAlmanacServiceCacheKey() { $service_phid = $this->getAlmanacServicePHID(); if (!$service_phid) {