Page MenuHomePhabricator

D20539.diff
No OneTemporary

D20539.diff

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
@@ -1000,6 +1000,7 @@
'DiffusionSetPasswordSettingsPanel' => 'applications/diffusion/panel/DiffusionSetPasswordSettingsPanel.php',
'DiffusionSetupException' => 'applications/diffusion/exception/DiffusionSetupException.php',
'DiffusionSourceHyperlinkEngineExtension' => 'applications/diffusion/engineextension/DiffusionSourceHyperlinkEngineExtension.php',
+ 'DiffusionSourceLinkRemarkupRule' => 'applications/diffusion/remarkup/DiffusionSourceLinkRemarkupRule.php',
'DiffusionSourceLinkView' => 'applications/diffusion/view/DiffusionSourceLinkView.php',
'DiffusionSubversionCommandEngine' => 'applications/diffusion/protocol/DiffusionSubversionCommandEngine.php',
'DiffusionSubversionSSHWorkflow' => 'applications/diffusion/ssh/DiffusionSubversionSSHWorkflow.php',
@@ -6685,6 +6686,7 @@
'DiffusionSetPasswordSettingsPanel' => 'PhabricatorSettingsPanel',
'DiffusionSetupException' => 'Exception',
'DiffusionSourceHyperlinkEngineExtension' => 'PhabricatorRemarkupHyperlinkEngineExtension',
+ 'DiffusionSourceLinkRemarkupRule' => 'PhutilRemarkupRule',
'DiffusionSourceLinkView' => 'AphrontView',
'DiffusionSubversionCommandEngine' => 'DiffusionCommandEngine',
'DiffusionSubversionSSHWorkflow' => 'DiffusionSSHWorkflow',
diff --git a/src/applications/diffusion/application/PhabricatorDiffusionApplication.php b/src/applications/diffusion/application/PhabricatorDiffusionApplication.php
--- a/src/applications/diffusion/application/PhabricatorDiffusionApplication.php
+++ b/src/applications/diffusion/application/PhabricatorDiffusionApplication.php
@@ -40,6 +40,7 @@
new DiffusionCommitRemarkupRule(),
new DiffusionRepositoryRemarkupRule(),
new DiffusionRepositoryByIDRemarkupRule(),
+ new DiffusionSourceLinkRemarkupRule(),
);
}
diff --git a/src/applications/diffusion/remarkup/DiffusionSourceLinkRemarkupRule.php b/src/applications/diffusion/remarkup/DiffusionSourceLinkRemarkupRule.php
new file mode 100644
--- /dev/null
+++ b/src/applications/diffusion/remarkup/DiffusionSourceLinkRemarkupRule.php
@@ -0,0 +1,221 @@
+<?php
+
+final class DiffusionSourceLinkRemarkupRule
+ extends PhutilRemarkupRule {
+
+ const KEY_SOURCELINKS = 'diffusion.links';
+
+ public function getPriority() {
+ return 200.0;
+ }
+
+ public function apply($text) {
+ return preg_replace_callback(
+ '@{(?:src|source)\b((?:[^}\\\\]+|\\\\.)*)}@m',
+ array($this, 'markupSourceLink'),
+ $text);
+ }
+
+ public function markupSourceLink(array $matches) {
+ $engine = $this->getEngine();
+ $text_mode = $engine->isTextMode();
+ $mail_mode = $engine->isHTMLMailMode();
+
+ if (!$this->isFlatText($matches[0]) || $text_mode || $mail_mode) {
+ // We could do better than this in text mode and mail mode, but focus
+ // on web mode first.
+ return $matches[0];
+ }
+
+ $metadata_key = self::KEY_SOURCELINKS;
+ $metadata = $engine->getTextMetadata($metadata_key, array());
+
+ $token = $engine->storeText($matches[0]);
+
+ $metadata[] = array(
+ 'token' => $token,
+ 'raw' => $matches[0],
+ 'input' => $matches[1],
+ );
+
+ $engine->setTextMetadata($metadata_key, $metadata);
+
+ return $token;
+ }
+
+ public function didMarkupText() {
+ $engine = $this->getEngine();
+ $metadata_key = self::KEY_SOURCELINKS;
+ $metadata = $engine->getTextMetadata($metadata_key, array());
+
+ if (!$metadata) {
+ return;
+ }
+
+ $viewer = $engine->getConfig('viewer');
+ if (!$viewer) {
+ return;
+ }
+
+ $defaults = array(
+ 'repository' => null,
+ 'line' => null,
+ 'commit' => null,
+ 'ref' => null,
+ );
+
+ $tags = array();
+ foreach ($metadata as $ref) {
+ $token = $ref['token'];
+ $raw = $ref['raw'];
+ $input = $ref['input'];
+
+ $pattern =
+ '(^'.
+ '[\s,]*'.
+ '(?:"(?P<quotedpath>(?:[^\\\\"]+|\\.)+)"|(?P<rawpath>[^\s,]+))'.
+ '[\s,]*'.
+ '(?P<options>.*)'.
+ '\z)';
+ $matches = null;
+ if (!preg_match($pattern, $input, $matches)) {
+ $hint_text = pht(
+ 'Missing path, expected "{src path ...}" in: %s',
+ $raw);
+ $hint = $this->newSyntaxHint($hint_text);
+
+ $engine->overwriteStoredText($token, $hint);
+ continue;
+ }
+
+ $path = idx($matches, 'rawpath');
+ if (!strlen($path)) {
+ $path = idx($matches, 'quotedpath');
+ $path = stripcslashes($path);
+ }
+
+ $parts = explode(':', $path, 2);
+ if (count($parts) == 2) {
+ $repository = nonempty($parts[0], null);
+ $path = $parts[1];
+ } else {
+ $repository = null;
+ $path = $parts[0];
+ }
+
+ $options = $matches['options'];
+
+ $parser = new PhutilSimpleOptions();
+ $options = $parser->parse($options) + $defaults;
+
+ foreach ($options as $key => $value) {
+ if (!array_key_exists($key, $defaults)) {
+ $hint_text = pht(
+ 'Unknown option "%s" in: %s',
+ $key,
+ $raw);
+ $hint = $this->newSyntaxHint($hint_text);
+
+ $engine->overwriteStoredText($token, $hint);
+ continue 2;
+ }
+ }
+
+ if ($options['repository'] !== null) {
+ $repository = $options['repository'];
+ }
+
+ if ($repository === null) {
+ $hint_text = pht(
+ 'Missing repository, expected "{src repository:path ...}" '.
+ 'or "{src path repository=...}" in: %s',
+ $raw);
+ $hint = $this->newSyntaxHint($hint_text);
+
+ $engine->overwriteStoredText($token, $hint);
+ continue;
+ }
+
+ $tags[] = array(
+ 'token' => $token,
+ 'raw' => $raw,
+ 'identifier' => $repository,
+ 'path' => $path,
+ 'options' => $options,
+ );
+ }
+
+ if (!$tags) {
+ return;
+ }
+
+ $query = id(new PhabricatorRepositoryQuery())
+ ->setViewer($viewer)
+ ->withIdentifiers(ipull($tags, 'identifier'));
+
+ $query->execute();
+
+ $repository_map = $query->getIdentifierMap();
+
+ foreach ($tags as $tag) {
+ $token = $tag['token'];
+
+ $identifier = $tag['identifier'];
+ $repository = idx($repository_map, $identifier);
+ if (!$repository) {
+ // For now, just bail out here. Ideally, we should distingiush between
+ // restricted and invalid repositories.
+ continue;
+ }
+
+ $drequest = DiffusionRequest::newFromDictionary(
+ array(
+ 'user' => $viewer,
+ 'repository' => $repository,
+ ));
+
+ $options = $tag['options'];
+
+ $line = $options['line'];
+ $commit = $options['commit'];
+ $ref_name = $options['ref'];
+
+ $link_uri = $drequest->generateURI(
+ array(
+ 'action' => 'browse',
+ 'path' => $tag['path'],
+ 'commit' => $commit,
+ 'line' => $line,
+ 'branch' => $ref_name,
+ ));
+
+ $view = id(new DiffusionSourceLinkView())
+ ->setRepository($repository)
+ ->setPath($tag['path'])
+ ->setURI($link_uri);
+
+ if ($line !== null) {
+ $view->setLine($line);
+ }
+
+ if ($commit !== null) {
+ $view->setCommit($commit);
+ }
+
+ if ($ref_name !== null) {
+ $view->setRefName($ref_name);
+ }
+
+ $engine->overwriteStoredText($token, $view);
+ }
+ }
+
+ private function newSyntaxHint($text) {
+ return id(new PHUITagView())
+ ->setType(PHUITagView::TYPE_SHADE)
+ ->setColor('red')
+ ->setIcon('fa-exclamation-triangle')
+ ->setName($text);
+ }
+
+}

File Metadata

Mime Type
text/plain
Expires
Wed, Mar 26, 5:16 AM (5 d, 58 s ago)
Storage Engine
blob
Storage Format
Encrypted (AES-256-CBC)
Storage Handle
7689604
Default Alt Text
D20539.diff (7 KB)

Event Timeline