Page Menu
Home
Phabricator
Search
Configure Global Search
Log In
Files
F15383016
D20528.diff
No One
Temporary
Actions
View File
Edit File
Delete File
View Transforms
Subscribe
Mute Notifications
Award Token
Flag For Later
Size
19 KB
Referenced Files
None
Subscribers
None
D20528.diff
View Options
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
@@ -1072,7 +1072,6 @@
'DivinerSymbolRemarkupRule' => 'applications/diviner/markup/DivinerSymbolRemarkupRule.php',
'DivinerWorkflow' => 'applications/diviner/workflow/DivinerWorkflow.php',
'DoorkeeperAsanaFeedWorker' => 'applications/doorkeeper/worker/DoorkeeperAsanaFeedWorker.php',
- 'DoorkeeperAsanaRemarkupRule' => 'applications/doorkeeper/remarkup/DoorkeeperAsanaRemarkupRule.php',
'DoorkeeperBridge' => 'applications/doorkeeper/bridge/DoorkeeperBridge.php',
'DoorkeeperBridgeAsana' => 'applications/doorkeeper/bridge/DoorkeeperBridgeAsana.php',
'DoorkeeperBridgeGitHub' => 'applications/doorkeeper/bridge/DoorkeeperBridgeGitHub.php',
@@ -1088,15 +1087,16 @@
'DoorkeeperExternalObjectQuery' => 'applications/doorkeeper/query/DoorkeeperExternalObjectQuery.php',
'DoorkeeperFeedStoryPublisher' => 'applications/doorkeeper/engine/DoorkeeperFeedStoryPublisher.php',
'DoorkeeperFeedWorker' => 'applications/doorkeeper/worker/DoorkeeperFeedWorker.php',
+ 'DoorkeeperHyperlinkEngineExtension' => 'applications/doorkeeper/engineextension/DoorkeeperHyperlinkEngineExtension.php',
'DoorkeeperImportEngine' => 'applications/doorkeeper/engine/DoorkeeperImportEngine.php',
'DoorkeeperJIRAFeedWorker' => 'applications/doorkeeper/worker/DoorkeeperJIRAFeedWorker.php',
- 'DoorkeeperJIRARemarkupRule' => 'applications/doorkeeper/remarkup/DoorkeeperJIRARemarkupRule.php',
'DoorkeeperMissingLinkException' => 'applications/doorkeeper/exception/DoorkeeperMissingLinkException.php',
'DoorkeeperObjectRef' => 'applications/doorkeeper/engine/DoorkeeperObjectRef.php',
- 'DoorkeeperRemarkupRule' => 'applications/doorkeeper/remarkup/DoorkeeperRemarkupRule.php',
+ 'DoorkeeperRemarkupURIInterface' => 'applications/doorkeeper/interface/DoorkeeperRemarkupURIInterface.php',
'DoorkeeperSchemaSpec' => 'applications/doorkeeper/storage/DoorkeeperSchemaSpec.php',
'DoorkeeperTagView' => 'applications/doorkeeper/view/DoorkeeperTagView.php',
'DoorkeeperTagsController' => 'applications/doorkeeper/controller/DoorkeeperTagsController.php',
+ 'DoorkeeperURIRef' => 'applications/doorkeeper/engine/DoorkeeperURIRef.php',
'DrydockAcquiredBrokenResourceException' => 'applications/drydock/exception/DrydockAcquiredBrokenResourceException.php',
'DrydockAlmanacServiceHostBlueprintImplementation' => 'applications/drydock/blueprint/DrydockAlmanacServiceHostBlueprintImplementation.php',
'DrydockApacheWebrootInterface' => 'applications/drydock/interface/webroot/DrydockApacheWebrootInterface.php',
@@ -6761,7 +6761,6 @@
'DivinerSymbolRemarkupRule' => 'PhutilRemarkupRule',
'DivinerWorkflow' => 'PhabricatorManagementWorkflow',
'DoorkeeperAsanaFeedWorker' => 'DoorkeeperFeedWorker',
- 'DoorkeeperAsanaRemarkupRule' => 'DoorkeeperRemarkupRule',
'DoorkeeperBridge' => 'Phobject',
'DoorkeeperBridgeAsana' => 'DoorkeeperBridge',
'DoorkeeperBridgeGitHub' => 'DoorkeeperBridge',
@@ -6779,15 +6778,15 @@
'DoorkeeperExternalObjectQuery' => 'PhabricatorCursorPagedPolicyAwareQuery',
'DoorkeeperFeedStoryPublisher' => 'Phobject',
'DoorkeeperFeedWorker' => 'FeedPushWorker',
+ 'DoorkeeperHyperlinkEngineExtension' => 'PhutilRemarkupHyperlinkEngineExtension',
'DoorkeeperImportEngine' => 'Phobject',
'DoorkeeperJIRAFeedWorker' => 'DoorkeeperFeedWorker',
- 'DoorkeeperJIRARemarkupRule' => 'DoorkeeperRemarkupRule',
'DoorkeeperMissingLinkException' => 'Exception',
'DoorkeeperObjectRef' => 'Phobject',
- 'DoorkeeperRemarkupRule' => 'PhutilRemarkupRule',
'DoorkeeperSchemaSpec' => 'PhabricatorConfigSchemaSpec',
'DoorkeeperTagView' => 'AphrontView',
'DoorkeeperTagsController' => 'PhabricatorController',
+ 'DoorkeeperURIRef' => 'Phobject',
'DrydockAcquiredBrokenResourceException' => 'Exception',
'DrydockAlmanacServiceHostBlueprintImplementation' => 'DrydockBlueprintImplementation',
'DrydockApacheWebrootInterface' => 'DrydockWebrootInterface',
@@ -8090,7 +8089,10 @@
'PhabricatorApplicationsController' => 'PhabricatorController',
'PhabricatorApplicationsListController' => 'PhabricatorApplicationsController',
'PhabricatorApplyEditField' => 'PhabricatorEditField',
- 'PhabricatorAsanaAuthProvider' => 'PhabricatorOAuth2AuthProvider',
+ 'PhabricatorAsanaAuthProvider' => array(
+ 'PhabricatorOAuth2AuthProvider',
+ 'DoorkeeperRemarkupURIInterface',
+ ),
'PhabricatorAsanaConfigOptions' => 'PhabricatorApplicationConfigOptions',
'PhabricatorAsanaSubtaskHasObjectEdgeType' => 'PhabricatorEdgeType',
'PhabricatorAsanaTaskHasObjectEdgeType' => 'PhabricatorEdgeType',
@@ -9569,7 +9571,10 @@
'PhabricatorIteratedMD5PasswordHasher' => 'PhabricatorPasswordHasher',
'PhabricatorIteratedMD5PasswordHasherTestCase' => 'PhabricatorTestCase',
'PhabricatorIteratorFileUploadSource' => 'PhabricatorFileUploadSource',
- 'PhabricatorJIRAAuthProvider' => 'PhabricatorOAuth1AuthProvider',
+ 'PhabricatorJIRAAuthProvider' => array(
+ 'PhabricatorOAuth1AuthProvider',
+ 'DoorkeeperRemarkupURIInterface',
+ ),
'PhabricatorJSONConfigType' => 'PhabricatorTextConfigType',
'PhabricatorJSONDocumentEngine' => 'PhabricatorTextDocumentEngine',
'PhabricatorJSONExportFormat' => 'PhabricatorExportFormat',
diff --git a/src/applications/auth/provider/PhabricatorAsanaAuthProvider.php b/src/applications/auth/provider/PhabricatorAsanaAuthProvider.php
--- a/src/applications/auth/provider/PhabricatorAsanaAuthProvider.php
+++ b/src/applications/auth/provider/PhabricatorAsanaAuthProvider.php
@@ -1,6 +1,8 @@
<?php
-final class PhabricatorAsanaAuthProvider extends PhabricatorOAuth2AuthProvider {
+final class PhabricatorAsanaAuthProvider
+ extends PhabricatorOAuth2AuthProvider
+ implements DoorkeeperRemarkupURIInterface {
public function getProviderName() {
return pht('Asana');
@@ -46,4 +48,26 @@
return null;
}
+/* -( DoorkeeperRemarkupURIInterface )------------------------------------- */
+
+ public function getDoorkeeperURIRef(PhutilURI $uri) {
+ $uri_string = phutil_string_cast($uri);
+
+ $pattern = '(https://app\\.asana\\.com/0/(\\d+)/(\\d+))';
+ $matches = null;
+ if (!preg_match($pattern, $uri_string, $matches)) {
+ return null;
+ }
+
+ $context_id = $matches[1];
+ $task_id = $matches[2];
+
+ return id(new DoorkeeperURIRef())
+ ->setURI($uri)
+ ->setApplicationType(DoorkeeperBridgeAsana::APPTYPE_ASANA)
+ ->setApplicationDomain(DoorkeeperBridgeAsana::APPDOMAIN_ASANA)
+ ->setObjectType(DoorkeeperBridgeAsana::OBJTYPE_TASK)
+ ->setObjectID($task_id);
+ }
+
}
diff --git a/src/applications/auth/provider/PhabricatorJIRAAuthProvider.php b/src/applications/auth/provider/PhabricatorJIRAAuthProvider.php
--- a/src/applications/auth/provider/PhabricatorJIRAAuthProvider.php
+++ b/src/applications/auth/provider/PhabricatorJIRAAuthProvider.php
@@ -1,10 +1,8 @@
<?php
-final class PhabricatorJIRAAuthProvider extends PhabricatorOAuth1AuthProvider {
-
- public function getJIRABaseURI() {
- return $this->getProviderConfig()->getProperty(self::PROPERTY_JIRA_URI);
- }
+final class PhabricatorJIRAAuthProvider
+ extends PhabricatorOAuth1AuthProvider
+ implements DoorkeeperRemarkupURIInterface {
public function getProviderName() {
return pht('JIRA');
@@ -332,4 +330,33 @@
return $config->getProperty(self::PROPERTY_REPORT_COMMENT, true);
}
+/* -( DoorkeeperRemarkupURIInterface )------------------------------------- */
+
+ public function getDoorkeeperURIRef(PhutilURI $uri) {
+ $uri_string = phutil_string_cast($uri);
+
+ $pattern = '((https?://\S+?)/browse/([A-Z]+-[1-9]\d*))';
+ $matches = null;
+ if (!preg_match($pattern, $uri_string, $matches)) {
+ return null;
+ }
+
+ $domain = $matches[1];
+ $issue = $matches[2];
+
+ $config = $this->getProviderConfig();
+ $base_uri = $config->getProperty(self::PROPERTY_JIRA_URI);
+
+ if ($domain !== rtrim($base_uri, '/')) {
+ return null;
+ }
+
+ return id(new DoorkeeperURIRef())
+ ->setURI($uri)
+ ->setApplicationType(DoorkeeperBridgeJIRA::APPTYPE_JIRA)
+ ->setApplicationDomain($this->getProviderDomain())
+ ->setObjectType(DoorkeeperBridgeJIRA::OBJTYPE_ISSUE)
+ ->setObjectID($issue);
+ }
+
}
diff --git a/src/applications/doorkeeper/application/PhabricatorDoorkeeperApplication.php b/src/applications/doorkeeper/application/PhabricatorDoorkeeperApplication.php
--- a/src/applications/doorkeeper/application/PhabricatorDoorkeeperApplication.php
+++ b/src/applications/doorkeeper/application/PhabricatorDoorkeeperApplication.php
@@ -22,13 +22,6 @@
return pht('Connect to Other Software');
}
- public function getRemarkupRules() {
- return array(
- new DoorkeeperAsanaRemarkupRule(),
- new DoorkeeperJIRARemarkupRule(),
- );
- }
-
public function getRoutes() {
return array(
'/doorkeeper/' => array(
diff --git a/src/applications/doorkeeper/engine/DoorkeeperURIRef.php b/src/applications/doorkeeper/engine/DoorkeeperURIRef.php
new file mode 100644
--- /dev/null
+++ b/src/applications/doorkeeper/engine/DoorkeeperURIRef.php
@@ -0,0 +1,91 @@
+<?php
+
+final class DoorkeeperURIRef extends Phobject {
+
+ private $uri;
+ private $applicationType;
+ private $applicationDomain;
+ private $objectType;
+ private $objectID;
+ private $text;
+ private $displayMode = self::DISPLAY_FULL;
+
+ const DISPLAY_FULL = 'full';
+ const DISPLAY_SHORT = 'short';
+
+ public function setURI(PhutilURI $uri) {
+ $this->uri = $uri;
+ return $this;
+ }
+
+ public function getURI() {
+ return $this->uri;
+ }
+
+ public function setApplicationType($application_type) {
+ $this->applicationType = $application_type;
+ return $this;
+ }
+
+ public function getApplicationType() {
+ return $this->applicationType;
+ }
+
+ public function setApplicationDomain($application_domain) {
+ $this->applicationDomain = $application_domain;
+ return $this;
+ }
+
+ public function getApplicationDomain() {
+ return $this->applicationDomain;
+ }
+
+ public function setObjectType($object_type) {
+ $this->objectType = $object_type;
+ return $this;
+ }
+
+ public function getObjectType() {
+ return $this->objectType;
+ }
+
+ public function setObjectID($object_id) {
+ $this->objectID = $object_id;
+ return $this;
+ }
+
+ public function getObjectID() {
+ return $this->objectID;
+ }
+
+ public function setText($text) {
+ $this->text = $text;
+ return $this;
+ }
+
+ public function getText() {
+ return $this->text;
+ }
+
+ public function setDisplayMode($display_mode) {
+ $options = array(
+ self::DISPLAY_FULL => true,
+ self::DISPLAY_SHORT => true,
+ );
+
+ if (!isset($options[$display_mode])) {
+ throw new Exception(
+ pht(
+ 'DoorkeeperURIRef display mode "%s" is unknown.',
+ $display_mode));
+ }
+
+ $this->displayMode = $display_mode;
+ return $this;
+ }
+
+ public function getDisplayMode() {
+ return $this->displayMode;
+ }
+
+}
diff --git a/src/applications/doorkeeper/engineextension/DoorkeeperHyperlinkEngineExtension.php b/src/applications/doorkeeper/engineextension/DoorkeeperHyperlinkEngineExtension.php
new file mode 100644
--- /dev/null
+++ b/src/applications/doorkeeper/engineextension/DoorkeeperHyperlinkEngineExtension.php
@@ -0,0 +1,92 @@
+<?php
+
+final class DoorkeeperHyperlinkEngineExtension
+ extends PhutilRemarkupHyperlinkEngineExtension {
+
+ const LINKENGINEKEY = 'doorkeeper';
+
+ public function processHyperlinks(array $hyperlinks) {
+ $engine = $this->getEngine();
+ $viewer = $engine->getConfig('viewer');
+
+ if (!$viewer) {
+ return;
+ }
+
+ $configs = id(new PhabricatorAuthProviderConfigQuery())
+ ->setViewer($viewer)
+ ->withIsEnabled(true)
+ ->execute();
+
+ $providers = array();
+ foreach ($configs as $key => $config) {
+ $provider = $config->getProvider();
+ if (($provider instanceof DoorkeeperRemarkupURIInterface)) {
+ $providers[] = $provider;
+ }
+ }
+
+ if (!$providers) {
+ return;
+ }
+
+ $refs = array();
+ foreach ($hyperlinks as $hyperlink) {
+ $uri = $hyperlink->getURI();
+ $uri = new PhutilURI($uri);
+
+ foreach ($providers as $provider) {
+ $ref = $provider->getDoorkeeperURIRef($uri);
+
+ if (($ref !== null) && !($ref instanceof DoorkeeperURIRef)) {
+ throw new Exception(
+ pht(
+ 'Expected "getDoorkeeperURIRef()" to return "null" or an '.
+ 'object of type "DoorkeeperURIRef", but got %s from provider '.
+ '"%s".',
+ phutil_describe_type($ref),
+ get_class($provider)));
+ }
+
+ if ($ref === null) {
+ continue;
+ }
+
+ $tag_id = celerity_generate_unique_node_id();
+ $href = phutil_string_cast($ref->getURI());
+
+ $refs[] = array(
+ 'id' => $tag_id,
+ 'href' => $href,
+ 'ref' => array(
+ $ref->getApplicationType(),
+ $ref->getApplicationDomain(),
+ $ref->getObjectType(),
+ $ref->getObjectID(),
+ ),
+ 'view' => $ref->getDisplayMode(),
+ );
+
+ $text = $ref->getText();
+ if ($text === null) {
+ $text = $href;
+ }
+
+ $view = id(new PHUITagView())
+ ->setID($tag_id)
+ ->setName($text)
+ ->setHref($href)
+ ->setType(PHUITagView::TYPE_OBJECT)
+ ->setExternal(true);
+
+ $hyperlink->setResult($view);
+ break;
+ }
+ }
+
+ if ($refs) {
+ Javelin::initBehavior('doorkeeper-tag', array('tags' => $refs));
+ }
+ }
+
+}
diff --git a/src/applications/doorkeeper/interface/DoorkeeperRemarkupURIInterface.php b/src/applications/doorkeeper/interface/DoorkeeperRemarkupURIInterface.php
new file mode 100644
--- /dev/null
+++ b/src/applications/doorkeeper/interface/DoorkeeperRemarkupURIInterface.php
@@ -0,0 +1,7 @@
+<?php
+
+interface DoorkeeperRemarkupURIInterface {
+
+ public function getDoorkeeperURIRef(PhutilURI $uri);
+
+}
diff --git a/src/applications/doorkeeper/remarkup/DoorkeeperAsanaRemarkupRule.php b/src/applications/doorkeeper/remarkup/DoorkeeperAsanaRemarkupRule.php
deleted file mode 100644
--- a/src/applications/doorkeeper/remarkup/DoorkeeperAsanaRemarkupRule.php
+++ /dev/null
@@ -1,31 +0,0 @@
-<?php
-
-final class DoorkeeperAsanaRemarkupRule
- extends DoorkeeperRemarkupRule {
-
- public function apply($text) {
- return preg_replace_callback(
- '@https://app\\.asana\\.com/0/(\\d+)/(\\d+)@',
- array($this, 'markupAsanaLink'),
- $text);
- }
-
- public function markupAsanaLink($matches) {
- return $this->addDoorkeeperTag(
- array(
- 'href' => $matches[0],
- 'tag' => array(
- 'ref' => array(
- DoorkeeperBridgeAsana::APPTYPE_ASANA,
- DoorkeeperBridgeAsana::APPDOMAIN_ASANA,
- DoorkeeperBridgeAsana::OBJTYPE_TASK,
- $matches[2],
- ),
- 'extra' => array(
- 'asana.context' => $matches[1],
- ),
- ),
- ));
- }
-
-}
diff --git a/src/applications/doorkeeper/remarkup/DoorkeeperJIRARemarkupRule.php b/src/applications/doorkeeper/remarkup/DoorkeeperJIRARemarkupRule.php
deleted file mode 100644
--- a/src/applications/doorkeeper/remarkup/DoorkeeperJIRARemarkupRule.php
+++ /dev/null
@@ -1,44 +0,0 @@
-<?php
-
-final class DoorkeeperJIRARemarkupRule
- extends DoorkeeperRemarkupRule {
-
- public function apply($text) {
- return preg_replace_callback(
- '@(https?://\S+?)/browse/([A-Z]+-[1-9]\d*)@',
- array($this, 'markupJIRALink'),
- $text);
- }
-
- public function markupJIRALink($matches) {
- $match_domain = $matches[1];
- $match_issue = $matches[2];
-
- // TODO: When we support multiple instances, deal with them here.
- $provider = PhabricatorJIRAAuthProvider::getJIRAProvider();
- if (!$provider) {
- return $matches[0];
- }
-
-
- $jira_base = $provider->getJIRABaseURI();
- if ($match_domain != rtrim($jira_base, '/')) {
- return $matches[0];
- }
-
- return $this->addDoorkeeperTag(
- array(
- 'href' => $matches[0],
- 'tag' => array(
- 'ref' => array(
- DoorkeeperBridgeJIRA::APPTYPE_JIRA,
- $provider->getProviderDomain(),
- DoorkeeperBridgeJIRA::OBJTYPE_ISSUE,
- $match_issue,
- ),
- ),
- ));
- }
-
-
-}
diff --git a/src/applications/doorkeeper/remarkup/DoorkeeperRemarkupRule.php b/src/applications/doorkeeper/remarkup/DoorkeeperRemarkupRule.php
deleted file mode 100644
--- a/src/applications/doorkeeper/remarkup/DoorkeeperRemarkupRule.php
+++ /dev/null
@@ -1,103 +0,0 @@
-<?php
-
-abstract class DoorkeeperRemarkupRule extends PhutilRemarkupRule {
-
- const KEY_TAGS = 'doorkeeper.tags';
-
- const VIEW_FULL = 'full';
- const VIEW_SHORT = 'short';
-
- public function getPriority() {
- return 350.0;
- }
-
- protected function addDoorkeeperTag(array $spec) {
- PhutilTypeSpec::checkMap(
- $spec,
- array(
- 'href' => 'string',
- 'tag' => 'map<string, wild>',
-
- 'name' => 'optional string',
- 'view' => 'optional string',
- ));
-
- $spec = $spec + array(
- 'view' => self::VIEW_FULL,
- );
-
- $views = array(
- self::VIEW_FULL,
- self::VIEW_SHORT,
- );
- $views = array_fuse($views);
- if (!isset($views[$spec['view']])) {
- throw new Exception(
- pht(
- 'Unsupported Doorkeeper tag view mode "%s". Supported modes are: %s.',
- $spec['view'],
- implode(', ', $views)));
- }
-
- $key = self::KEY_TAGS;
- $engine = $this->getEngine();
- $token = $engine->storeText(get_class($this));
-
- $tags = $engine->getTextMetadata($key, array());
-
- $tags[] = array(
- 'token' => $token,
- ) + $spec + array(
- 'extra' => array(),
- );
-
- $engine->setTextMetadata($key, $tags);
- return $token;
- }
-
- public function didMarkupText() {
- $key = self::KEY_TAGS;
- $engine = $this->getEngine();
- $tags = $engine->getTextMetadata($key, array());
-
- if (!$tags) {
- return;
- }
-
- $refs = array();
- foreach ($tags as $spec) {
- $href = $spec['href'];
- $name = idx($spec, 'name', $href);
-
- $this->assertFlatText($href);
- $this->assertFlatText($name);
-
- if ($this->getEngine()->isTextMode()) {
- $view = "{$name} <{$href}>";
- } else {
- $tag_id = celerity_generate_unique_node_id();
-
- $refs[] = array(
- 'id' => $tag_id,
- 'view' => $spec['view'],
- ) + $spec['tag'];
-
- $view = id(new PHUITagView())
- ->setID($tag_id)
- ->setName($name)
- ->setHref($href)
- ->setType(PHUITagView::TYPE_OBJECT)
- ->setExternal(true);
- }
-
- $engine->overwriteStoredText($spec['token'], $view);
- }
-
- if ($refs) {
- Javelin::initBehavior('doorkeeper-tag', array('tags' => $refs));
- }
-
- $engine->setTextMetadata($key, array());
- }
-
-}
File Metadata
Details
Attached
Mime Type
text/plain
Expires
Sat, Mar 15, 3:15 PM (1 w, 23 h ago)
Storage Engine
blob
Storage Format
Encrypted (AES-256-CBC)
Storage Handle
7600962
Default Alt Text
D20528.diff (19 KB)
Attached To
Mode
D20528: Implement Asana and JIRA external links via HyperlinkEngineExtension, not separate Remarkup rules
Attached
Detach File
Event Timeline
Log In to Comment