@recaptime-dev's working patches + fork for Phorge, a community fork of Phabricator. (Upstream dev and stable branches are at upstream/main and upstream/stable respectively.) hq.recaptime.dev/wiki/Phorge
phorge phabricator
1
fork

Configure Feed

Select the types of activity you want to include in your feed.

Allow Doorkeeper references to have multiple display variations (full, short, etc.)

Summary:
Ref T13102. An install has a custom rule for bridging JIRA references via Doorkeeper and would like to be able to render them as `JIRA-123` instead of `JIRA JIRA-123 Full JIRA title`.

I think it's reasonable to imagine future support upstream for `JIRA-123`, `{JIRA-123}`, and so on, although we do not support these today. We can take a small step toward eventual support by letting the rendering pipeline understand different view modes.

This adds an optional `name` (the default text rendered before we do the OAuth sync) and an optional `view`, which can be `short` or `full`.

Test Plan:
I tested this primarily with Asana, since it's less of a pain to set up than JIRA. The logic should be similar, hopefully.

I changed `DoorkeeperAsanaRemarkupRule` to specify `name` and `view`, e.g `'view' => (mt_rand(0, 1) ? 'short' : 'full')`. Then I made a bunch of Asana references in a comment and saw them randomly go short or long.

Maniphest Tasks: T13102

Differential Revision: https://secure.phabricator.com/D19215

+98 -30
+10 -10
resources/celerity/map.php
··· 10 10 'conpherence.pkg.css' => 'e68cf1fa', 11 11 'conpherence.pkg.js' => '15191c65', 12 12 'core.pkg.css' => 'c218ed53', 13 - 'core.pkg.js' => '8b7400e7', 13 + 'core.pkg.js' => '0fabde4f', 14 14 'darkconsole.pkg.js' => '1f9a31bc', 15 15 'differential.pkg.css' => '113e692c', 16 16 'differential.pkg.js' => 'f6d809c0', ··· 391 391 'rsrc/js/application/diffusion/behavior-load-blame.js' => '42126667', 392 392 'rsrc/js/application/diffusion/behavior-locate-file.js' => '6d3e1947', 393 393 'rsrc/js/application/diffusion/behavior-pull-lastmodified.js' => 'f01586dc', 394 - 'rsrc/js/application/doorkeeper/behavior-doorkeeper-tag.js' => 'e5822781', 394 + 'rsrc/js/application/doorkeeper/behavior-doorkeeper-tag.js' => '1db13e70', 395 395 'rsrc/js/application/drydock/drydock-live-operation-status.js' => '901935ef', 396 396 'rsrc/js/application/files/behavior-icon-composer.js' => '8499b6ab', 397 397 'rsrc/js/application/files/behavior-launch-icon-composer.js' => '48086888', ··· 608 608 'javelin-behavior-diffusion-jump-to' => '73d09eef', 609 609 'javelin-behavior-diffusion-locate-file' => '6d3e1947', 610 610 'javelin-behavior-diffusion-pull-lastmodified' => 'f01586dc', 611 - 'javelin-behavior-doorkeeper-tag' => 'e5822781', 611 + 'javelin-behavior-doorkeeper-tag' => '1db13e70', 612 612 'javelin-behavior-drydock-live-operation-status' => '901935ef', 613 613 'javelin-behavior-durable-column' => '2ae077e1', 614 614 'javelin-behavior-editengine-reorder-configs' => 'd7a74243', ··· 1023 1023 'javelin-vector', 1024 1024 'javelin-request', 1025 1025 'javelin-uri', 1026 + ), 1027 + '1db13e70' => array( 1028 + 'javelin-behavior', 1029 + 'javelin-dom', 1030 + 'javelin-json', 1031 + 'javelin-workflow', 1032 + 'javelin-magical-init', 1026 1033 ), 1027 1034 '1f6794f6' => array( 1028 1035 'javelin-behavior', ··· 2086 2093 'e4cc26b3' => array( 2087 2094 'javelin-behavior', 2088 2095 'javelin-dom', 2089 - ), 2090 - 'e5822781' => array( 2091 - 'javelin-behavior', 2092 - 'javelin-dom', 2093 - 'javelin-json', 2094 - 'javelin-workflow', 2095 - 'javelin-magical-init', 2096 2096 ), 2097 2097 'e74b7517' => array( 2098 2098 'javelin-install',
+1
src/applications/doorkeeper/bridge/DoorkeeperBridgeJIRA.php
··· 97 97 98 98 $ref->setAttribute('title', idx($fields, 'summary')); 99 99 $ref->setAttribute('description', idx($result, 'description')); 100 + $ref->setAttribute('shortname', $result['key']); 100 101 101 102 $obj = $ref->getExternalObject(); 102 103 if ($obj->getID()) {
+16 -9
src/applications/doorkeeper/controller/DoorkeeperTagsController.php
··· 13 13 } 14 14 15 15 $refs = array(); 16 - $id_map = array(); 17 - foreach ($tags as $tag_spec) { 16 + foreach ($tags as $key => $tag_spec) { 18 17 $tag = $tag_spec['ref']; 19 18 $ref = id(new DoorkeeperObjectRef()) 20 19 ->setApplicationType($tag[0]) 21 20 ->setApplicationDomain($tag[1]) 22 21 ->setObjectType($tag[2]) 23 22 ->setObjectID($tag[3]); 24 - 25 - $key = $ref->getObjectKey(); 26 - $id_map[$key] = $tag_spec['id']; 27 23 $refs[$key] = $ref; 28 24 } 29 25 ··· 43 39 continue; 44 40 } 45 41 46 - $id = $id_map[$key]; 42 + $tag_spec = $tags[$key]; 43 + 44 + $id = $tag_spec['id']; 45 + $view = idx($tag_spec, 'view'); 46 + 47 + $is_short = ($view == 'short'); 48 + 49 + if ($is_short) { 50 + $name = $ref->getShortName(); 51 + } else { 52 + $name = $ref->getFullName(); 53 + } 47 54 48 55 $tag = id(new PHUITagView()) 49 56 ->setID($id) 50 - ->setName($ref->getFullName()) 57 + ->setName($name) 51 58 ->setHref($uri) 52 59 ->setType(PHUITagView::TYPE_OBJECT) 53 60 ->setExternal(true) 54 61 ->render(); 55 62 56 63 $results[] = array( 57 - 'id' => $id, 58 - 'markup' => $tag, 64 + 'id' => $id, 65 + 'markup' => $tag, 59 66 ); 60 67 } 61 68
+7
src/applications/doorkeeper/engine/DoorkeeperObjectRef.php
··· 107 107 pht('External Object')); 108 108 } 109 109 110 + public function getShortName() { 111 + return coalesce( 112 + $this->getAttribute('shortname'), 113 + $this->getAttribute('name'), 114 + pht('External Object')); 115 + } 116 + 110 117 public function getObjectKey() { 111 118 if (!$this->objectKey) { 112 119 $this->objectKey = PhabricatorHash::digestForIndex(
+47 -8
src/applications/doorkeeper/remarkup/DoorkeeperRemarkupRule.php
··· 4 4 5 5 const KEY_TAGS = 'doorkeeper.tags'; 6 6 7 + const VIEW_FULL = 'full'; 8 + const VIEW_SHORT = 'short'; 9 + 7 10 public function getPriority() { 8 11 return 350.0; 9 12 } 10 13 11 14 protected function addDoorkeeperTag(array $spec) { 15 + PhutilTypeSpec::checkMap( 16 + $spec, 17 + array( 18 + 'href' => 'string', 19 + 'tag' => 'map<string, wild>', 20 + 21 + 'name' => 'optional string', 22 + 'view' => 'optional string', 23 + )); 24 + 25 + $spec = $spec + array( 26 + 'view' => self::VIEW_FULL, 27 + ); 28 + 29 + $views = array( 30 + self::VIEW_FULL, 31 + self::VIEW_SHORT, 32 + ); 33 + $views = array_fuse($views); 34 + if (!isset($views[$spec['view']])) { 35 + throw new Exception( 36 + pht( 37 + 'Unsupported Doorkeeper tag view mode "%s". Supported modes are: %s.', 38 + $spec['view'], 39 + implode(', ', $views))); 40 + } 41 + 12 42 $key = self::KEY_TAGS; 13 43 $engine = $this->getEngine(); 14 44 $token = $engine->storeText(get_class($this)); ··· 36 66 37 67 $refs = array(); 38 68 foreach ($tags as $spec) { 39 - $tag_id = celerity_generate_unique_node_id(); 69 + $href = $spec['href']; 70 + $name = idx($spec, 'name', $href); 40 71 41 - $refs[] = array( 42 - 'id' => $tag_id, 43 - ) + $spec['tag']; 72 + $this->assertFlatText($href); 73 + $this->assertFlatText($name); 44 74 45 75 if ($this->getEngine()->isTextMode()) { 46 - $view = $spec['href']; 76 + $view = "{$name} <{$href}>"; 47 77 } else { 78 + $tag_id = celerity_generate_unique_node_id(); 79 + 80 + $refs[] = array( 81 + 'id' => $tag_id, 82 + 'view' => $spec['view'], 83 + ) + $spec['tag']; 84 + 48 85 $view = id(new PHUITagView()) 49 86 ->setID($tag_id) 50 - ->setName($this->assertFlatText($spec['href'])) 51 - ->setHref($this->assertFlatText($spec['href'])) 87 + ->setName($name) 88 + ->setHref($href) 52 89 ->setType(PHUITagView::TYPE_OBJECT) 53 90 ->setExternal(true); 54 91 } ··· 56 93 $engine->overwriteStoredText($spec['token'], $view); 57 94 } 58 95 59 - Javelin::initBehavior('doorkeeper-tag', array('tags' => $refs)); 96 + if ($refs) { 97 + Javelin::initBehavior('doorkeeper-tag', array('tags' => $refs)); 98 + } 60 99 61 100 $engine->setTextMetadata($key, array()); 62 101 }
+17 -3
webroot/rsrc/js/application/doorkeeper/behavior-doorkeeper-tag.js
··· 40 40 }; 41 41 42 42 for (var ii = 0; ii < tags.length; ii++) { 43 - var tag_key = tags[ii].ref.join('@'); 43 + var key_parts = []; 44 + 45 + key_parts = key_parts.concat(tags[ii].ref); 46 + key_parts.push(tags[ii].view); 47 + 48 + var tag_key = key_parts.join(' '); 49 + 44 50 if (tag_key in statics.cache) { 45 - have.push({id: tags[ii].id, markup: statics.cache[tag_key]}); 51 + have.push( 52 + { 53 + id: tags[ii].id, 54 + markup: statics.cache[tag_key] 55 + }); 46 56 } else { 47 57 need.push(tags[ii]); 48 58 keys[tags[ii].id] = tag_key; ··· 54 64 } 55 65 56 66 if (need.length) { 57 - new JX.Workflow('/doorkeeper/tags/', {tags: JX.JSON.stringify(need)}) 67 + var data = { 68 + tags: JX.JSON.stringify(need) 69 + }; 70 + 71 + new JX.Workflow('/doorkeeper/tags/', data) 58 72 .setHandler(function(r) { draw(r.tags); }) 59 73 .start(); 60 74 }