@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.

Prepare for hovercards

Summary:
- Unify all the reference/embed Remarkup rules for Differential, Maniphest, Paste and Ponder.
- Add rules for Pholio.
- Does not yet unify Diffusion or Files (both are a bit more involved).
- Prepare for hovercards.

Test Plan: {F33894}

Reviewers: chad, vrana

Reviewed By: vrana

CC: aran

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

+274 -126
+4 -6
src/__phutil_library_map__.php
··· 1181 1181 'PhabricatorRedirectController' => 'applications/base/controller/PhabricatorRedirectController.php', 1182 1182 'PhabricatorRefreshCSRFController' => 'applications/auth/controller/PhabricatorRefreshCSRFController.php', 1183 1183 'PhabricatorRemarkupControl' => 'view/form/control/PhabricatorRemarkupControl.php', 1184 - 'PhabricatorRemarkupRuleDifferentialHandle' => 'applications/differential/remarkup/PhabricatorRemarkupRuleDifferentialHandle.php', 1185 1184 'PhabricatorRemarkupRuleEmbedFile' => 'applications/files/remarkup/PhabricatorRemarkupRuleEmbedFile.php', 1186 1185 'PhabricatorRemarkupRuleImageMacro' => 'applications/macro/remarkup/PhabricatorRemarkupRuleImageMacro.php', 1187 - 'PhabricatorRemarkupRuleManiphestHandle' => 'applications/maniphest/remarkup/PhabricatorRemarkupRuleManiphestHandle.php', 1188 1186 'PhabricatorRemarkupRuleMeme' => 'applications/macro/remarkup/PhabricatorRemarkupRuleMeme.php', 1189 1187 'PhabricatorRemarkupRuleMention' => 'applications/people/remarkup/PhabricatorRemarkupRuleMention.php', 1190 - 'PhabricatorRemarkupRuleObjectHandle' => 'infrastructure/markup/rule/PhabricatorRemarkupRuleObjectHandle.php', 1188 + 'PhabricatorRemarkupRuleObject' => 'infrastructure/markup/rule/PhabricatorRemarkupRuleObject.php', 1191 1189 'PhabricatorRemarkupRuleObjectName' => 'infrastructure/markup/rule/PhabricatorRemarkupRuleObjectName.php', 1192 1190 'PhabricatorRemarkupRuleYoutube' => 'infrastructure/markup/rule/PhabricatorRemarkupRuleYoutube.php', 1193 1191 'PhabricatorRepository' => 'applications/repository/storage/PhabricatorRepository.php', ··· 1462 1460 'PholioMockListController' => 'applications/pholio/controller/PholioMockListController.php', 1463 1461 'PholioMockQuery' => 'applications/pholio/query/PholioMockQuery.php', 1464 1462 'PholioMockViewController' => 'applications/pholio/controller/PholioMockViewController.php', 1463 + 'PholioRemarkupRule' => 'applications/pholio/remarkup/PholioRemarkupRule.php', 1465 1464 'PholioReplyHandler' => 'applications/pholio/mail/PholioReplyHandler.php', 1466 1465 'PholioSearchIndexer' => 'applications/pholio/search/PholioSearchIndexer.php', 1467 1466 'PholioTransaction' => 'applications/pholio/storage/PholioTransaction.php', ··· 2657 2656 'PhabricatorRedirectController' => 'PhabricatorController', 2658 2657 'PhabricatorRefreshCSRFController' => 'PhabricatorAuthController', 2659 2658 'PhabricatorRemarkupControl' => 'AphrontFormTextAreaControl', 2660 - 'PhabricatorRemarkupRuleDifferentialHandle' => 'PhabricatorRemarkupRuleObjectHandle', 2661 2659 'PhabricatorRemarkupRuleEmbedFile' => 'PhutilRemarkupRule', 2662 2660 'PhabricatorRemarkupRuleImageMacro' => 'PhutilRemarkupRule', 2663 - 'PhabricatorRemarkupRuleManiphestHandle' => 'PhabricatorRemarkupRuleObjectHandle', 2664 2661 'PhabricatorRemarkupRuleMeme' => 'PhutilRemarkupRule', 2665 2662 'PhabricatorRemarkupRuleMention' => 'PhutilRemarkupRule', 2666 - 'PhabricatorRemarkupRuleObjectHandle' => 'PhutilRemarkupRule', 2663 + 'PhabricatorRemarkupRuleObject' => 'PhutilRemarkupRule', 2667 2664 'PhabricatorRemarkupRuleObjectName' => 'PhutilRemarkupRule', 2668 2665 'PhabricatorRemarkupRuleYoutube' => 'PhutilRemarkupRule', 2669 2666 'PhabricatorRepository' => ··· 2948 2945 'PholioMockListController' => 'PholioController', 2949 2946 'PholioMockQuery' => 'PhabricatorCursorPagedPolicyAwareQuery', 2950 2947 'PholioMockViewController' => 'PholioController', 2948 + 'PholioRemarkupRule' => 'PhabricatorRemarkupRuleObject', 2951 2949 'PholioReplyHandler' => 'PhabricatorMailReplyHandler', 2952 2950 'PholioSearchIndexer' => 'PhabricatorSearchDocumentIndexer', 2953 2951 'PholioTransaction' => 'PhabricatorApplicationTransaction',
+4
src/applications/base/PhabricatorApplication.php
··· 152 152 } 153 153 } 154 154 155 + public function getRemarkupRules() { 156 + return array(); 157 + } 158 + 155 159 156 160 /* -( URI Routing )-------------------------------------------------------- */ 157 161
+6
src/applications/differential/application/PhabricatorApplicationDifferential.php
··· 73 73 return 0.100; 74 74 } 75 75 76 + public function getRemarkupRules() { 77 + return array( 78 + new DifferentialRemarkupRule(), 79 + ); 80 + } 81 + 76 82 public function loadStatus(PhabricatorUser $user) { 77 83 $revisions = id(new DifferentialRevisionQuery()) 78 84 ->withResponsibleUsers(array($user->getPHID()))
+10
src/applications/differential/query/DifferentialRevisionQuery.php
··· 60 60 private $needDiffIDs = false; 61 61 private $needCommitPHIDs = false; 62 62 private $needHashes = false; 63 + private $viewer; 64 + 65 + public function setViewer(PhabricatorUser $viewer) { 66 + $this->viewer = $viewer; 67 + return $this; 68 + } 69 + 70 + public function getViewer() { 71 + return $this->viewer; 72 + } 63 73 64 74 65 75 /* -( Query Configuration )------------------------------------------------ */
-21
src/applications/differential/remarkup/PhabricatorRemarkupRuleDifferentialHandle.php
··· 1 - <?php 2 - 3 - /** 4 - * @group markup 5 - */ 6 - final class PhabricatorRemarkupRuleDifferentialHandle 7 - extends PhabricatorRemarkupRuleObjectHandle { 8 - 9 - protected function getObjectNamePrefix() { 10 - return 'D'; 11 - } 12 - 13 - protected function loadObjectPHID($id) { 14 - $revision = id(new DifferentialRevision())->load($id); 15 - if ($revision) { 16 - return $revision->getPHID(); 17 - } 18 - return null; 19 - } 20 - 21 - }
+9
src/applications/maniphest/ManiphestTaskQuery.php
··· 58 58 private $rowCount = null; 59 59 60 60 private $groupByProjectResults = null; // See comment at bottom for details 61 + private $viewer; 61 62 63 + public function setViewer(PhabricatorUser $viewer) { 64 + $this->viewer = $viewer; 65 + return $this; 66 + } 67 + 68 + public function getViewer() { 69 + return $this->viewer; 70 + } 62 71 63 72 public function withAuthors(array $authors) { 64 73 $this->authorPHIDs = $authors;
+7 -1
src/applications/maniphest/application/PhabricatorApplicationManiphest.php
··· 38 38 39 39 public function getEventListeners() { 40 40 return array( 41 - new ManiphestPeopleMenuEventListener() 41 + new ManiphestPeopleMenuEventListener(), 42 + ); 43 + } 44 + 45 + public function getRemarkupRules() { 46 + return array( 47 + new ManiphestRemarkupRule(), 42 48 ); 43 49 } 44 50
-21
src/applications/maniphest/remarkup/PhabricatorRemarkupRuleManiphestHandle.php
··· 1 - <?php 2 - 3 - /** 4 - * @group markup 5 - */ 6 - final class PhabricatorRemarkupRuleManiphestHandle 7 - extends PhabricatorRemarkupRuleObjectHandle { 8 - 9 - protected function getObjectNamePrefix() { 10 - return 'T'; 11 - } 12 - 13 - protected function loadObjectPHID($id) { 14 - $task = id(new ManiphestTask())->load($id); 15 - if ($task) { 16 - return $task->getPHID(); 17 - } 18 - return null; 19 - } 20 - 21 - }
+6
src/applications/paste/application/PhabricatorApplicationPaste.php
··· 22 22 return $this->getBaseURI().'create/'; 23 23 } 24 24 25 + public function getRemarkupRules() { 26 + return array( 27 + new PhabricatorPasteRemarkupRule(), 28 + ); 29 + } 30 + 25 31 public function getRoutes() { 26 32 return array( 27 33 '/P(?P<id>[1-9]\d*)' => 'PhabricatorPasteViewController',
+6
src/applications/pholio/application/PhabricatorApplicationPholio.php
··· 34 34 return true; 35 35 } 36 36 37 + public function getRemarkupRules() { 38 + return array( 39 + new PholioRemarkupRule(), 40 + ); 41 + } 42 + 37 43 public function getRoutes() { 38 44 return array( 39 45 '/M(?P<id>[1-9]\d*)' => 'PholioMockViewController',
+23
src/applications/pholio/remarkup/PholioRemarkupRule.php
··· 1 + <?php 2 + 3 + final class PholioRemarkupRule 4 + extends PhabricatorRemarkupRuleObject { 5 + 6 + protected function getObjectNamePrefix() { 7 + return 'M'; 8 + } 9 + 10 + protected function loadObjects(array $ids) { 11 + $viewer = $this->getEngine()->getConfig('viewer'); 12 + 13 + if (!$viewer) { 14 + return array(); 15 + } 16 + 17 + return id(new PholioMockQuery()) 18 + ->setViewer($viewer) 19 + ->withIDs($ids) 20 + ->execute(); 21 + } 22 + 23 + }
+6
src/applications/ponder/application/PhabricatorApplicationPonder.php
··· 31 31 return $status; 32 32 } 33 33 34 + public function getRemarkupRules() { 35 + return array( 36 + new PonderRemarkupRule(), 37 + ); 38 + } 39 + 34 40 public function getApplicationGroup() { 35 41 return self::GROUP_COMMUNICATION; 36 42 }
+6 -11
src/infrastructure/markup/PhabricatorMarkupEngine.php
··· 400 400 $rules[] = new PhutilRemarkupRuleHyperlink(); 401 401 $rules[] = new PhrictionRemarkupRule(); 402 402 403 - $rules[] = new PhabricatorRemarkupRuleDifferentialHandle(); 404 - if (PhabricatorEnv::getEnvConfig('maniphest.enabled')) { 405 - $rules[] = new PhabricatorRemarkupRuleManiphestHandle(); 406 - } 407 - 408 403 $rules[] = new PhabricatorRemarkupRuleEmbedFile(); 409 404 410 - $rules[] = new DifferentialRemarkupRule(); 411 405 $rules[] = new DiffusionRemarkupRule(); 412 - if (PhabricatorEnv::getEnvConfig('maniphest.enabled')) { 413 - $rules[] = new ManiphestRemarkupRule(); 414 - } 415 - $rules[] = new PhabricatorPasteRemarkupRule(); 416 406 417 407 $rules[] = new PhabricatorCountdownRemarkupRule(); 418 408 419 - $rules[] = new PonderRemarkupRule(); 409 + $applications = PhabricatorApplication::getAllInstalledApplications(); 410 + foreach ($applications as $application) { 411 + foreach ($application->getRemarkupRules() as $rule) { 412 + $rules[] = $rule; 413 + } 414 + } 420 415 421 416 if ($options['macros']) { 422 417 $rules[] = new PhabricatorRemarkupRuleImageMacro();
+187
src/infrastructure/markup/rule/PhabricatorRemarkupRuleObject.php
··· 1 + <?php 2 + 3 + /** 4 + * @group markup 5 + */ 6 + abstract class PhabricatorRemarkupRuleObject 7 + extends PhutilRemarkupRule { 8 + 9 + const KEY_RULE_OBJECT = 'rule.object'; 10 + 11 + abstract protected function getObjectNamePrefix(); 12 + abstract protected function loadObjects(array $ids); 13 + 14 + protected function getObjectIDPattern() { 15 + return '[1-9]\d*'; 16 + } 17 + 18 + protected function shouldMarkupObject(array $params) { 19 + return true; 20 + } 21 + 22 + protected function loadHandles(array $objects) { 23 + $phids = mpull($objects, 'getPHID'); 24 + $query = new PhabricatorObjectHandleData($phids); 25 + 26 + $viewer = $this->getEngine()->getConfig('viewer'); 27 + if ($viewer) { 28 + $query->setViewer($viewer); 29 + } 30 + $handles = $query->loadHandles(); 31 + 32 + $result = array(); 33 + foreach ($objects as $id => $object) { 34 + $result[$id] = $handles[$object->getPHID()]; 35 + } 36 + return $result; 37 + } 38 + 39 + protected function renderObjectRef($object, $handle, $anchor) { 40 + $href = $handle->getURI(); 41 + $text = $this->getObjectNamePrefix().$object->getID(); 42 + if ($anchor) { 43 + $matches = null; 44 + if (preg_match('@^#(?:comment-)?(\d{1,7})$@', $anchor, $matches)) { 45 + // Maximum length is 7 because 12345678 could be a file hash in 46 + // Differential. 47 + $href = $href."#comment-".$matches[1]; 48 + $text = $text."#".$matches[1]; 49 + } else { 50 + $href = $href.$anchor; 51 + $text = $text.$anchor; 52 + } 53 + } 54 + 55 + $status_closed = PhabricatorObjectHandleStatus::STATUS_CLOSED; 56 + 57 + $attr = array( 58 + 'phid' => $handle->getPHID(), 59 + 'closed' => ($handle->getStatus() == $status_closed), 60 + ); 61 + 62 + return $this->renderHovertag($text, $href, $attr); 63 + } 64 + 65 + protected function renderObjectEmbed($object, $handle, $options) { 66 + $name = $handle->getFullName(); 67 + $href = $handle->getURI(); 68 + $attr = array( 69 + 'phid' => $handle->getPHID(), 70 + ); 71 + 72 + return $this->renderHovertag($name, $href, $attr); 73 + } 74 + 75 + protected function renderHovertag($name, $href, array $attr = array()) { 76 + return id(new PhabricatorTagView()) 77 + ->setName($name) 78 + ->setHref($href) 79 + ->setType(PhabricatorTagView::TYPE_OBJECT) 80 + ->setPHID(idx($attr, 'phid')) 81 + ->setClosed(idx($attr, 'closed')) 82 + ->render(); 83 + } 84 + 85 + public function apply($text) { 86 + $prefix = $this->getObjectNamePrefix(); 87 + $prefix = preg_quote($prefix, '@'); 88 + $id = $this->getObjectIDPattern(); 89 + 90 + $text = preg_replace_callback( 91 + '@\B{'.$prefix.'('.$id.')((?:[^}\\\\]|\\\\.)*)}\B@', 92 + array($this, 'markupObjectEmbed'), 93 + $text); 94 + 95 + $text = preg_replace_callback( 96 + '@\b'.$prefix.'('.$id.')(?:#([-\w\d]+))?\b@', 97 + array($this, 'markupObjectReference'), 98 + $text); 99 + 100 + return $text; 101 + } 102 + 103 + public function markupObjectEmbed($matches) { 104 + return $this->markupObject(array( 105 + 'type' => 'embed', 106 + 'id' => $matches[1], 107 + 'options' => idx($matches, 2), 108 + 'original' => $matches[0], 109 + )); 110 + } 111 + 112 + public function markupObjectReference($matches) { 113 + return $this->markupObject(array( 114 + 'type' => 'ref', 115 + 'id' => $matches[1], 116 + 'anchor' => idx($matches, 2), 117 + 'original' => $matches[0], 118 + )); 119 + } 120 + 121 + private function markupObject(array $params) { 122 + if (!$this->shouldMarkupObject($params)) { 123 + return $params['original']; 124 + } 125 + 126 + $engine = $this->getEngine(); 127 + $token = $engine->storeText('x'); 128 + 129 + $metadata_key = self::KEY_RULE_OBJECT.'.'.$this->getObjectNamePrefix(); 130 + $metadata = $engine->getTextMetadata($metadata_key, array()); 131 + 132 + $metadata[] = array( 133 + 'token' => $token, 134 + ) + $params; 135 + 136 + $engine->setTextMetadata($metadata_key, $metadata); 137 + 138 + return $token; 139 + } 140 + 141 + public function didMarkupText() { 142 + $engine = $this->getEngine(); 143 + $metadata_key = self::KEY_RULE_OBJECT.'.'.$this->getObjectNamePrefix(); 144 + $metadata = $engine->getTextMetadata($metadata_key, array()); 145 + 146 + if (!$metadata) { 147 + return; 148 + } 149 + 150 + 151 + $ids = ipull($metadata, 'id'); 152 + $objects = $this->loadObjects($ids); 153 + 154 + // For objects that are invalid or which the user can't see, just render 155 + // the original text. 156 + 157 + // TODO: We should probably distinguish between these cases and render a 158 + // "you can't see this" state for nonvisible objects. 159 + 160 + foreach ($metadata as $key => $spec) { 161 + if (empty($objects[$spec['id']])) { 162 + $engine->overwriteStoredText( 163 + $spec['token'], 164 + $spec['original']); 165 + unset($metadata[$key]); 166 + } 167 + } 168 + 169 + $handles = $this->loadHandles($objects); 170 + foreach ($metadata as $key => $spec) { 171 + $handle = $handles[$spec['id']]; 172 + $object = $objects[$spec['id']]; 173 + switch ($spec['type']) { 174 + case 'ref': 175 + $view = $this->renderObjectRef($object, $handle, $spec['anchor']); 176 + break; 177 + case 'embed': 178 + $view = $this->renderObjectEmbed($object, $handle, $spec['options']); 179 + break; 180 + } 181 + $engine->overwriteStoredText($spec['token'], $view); 182 + } 183 + 184 + $engine->setTextMetadata($metadata_key, array()); 185 + } 186 + 187 + }
-66
src/infrastructure/markup/rule/PhabricatorRemarkupRuleObjectHandle.php
··· 1 - <?php 2 - 3 - /** 4 - * @group markup 5 - */ 6 - abstract class PhabricatorRemarkupRuleObjectHandle 7 - extends PhutilRemarkupRule { 8 - 9 - const KEY_RULE_HANDLE = 'rule.handle'; 10 - 11 - abstract protected function getObjectNamePrefix(); 12 - abstract protected function loadObjectPHID($id); 13 - 14 - public function apply($text) { 15 - $prefix = $this->getObjectNamePrefix(); 16 - return preg_replace_callback( 17 - "@\B{{$prefix}(\d+)}\B@", 18 - array($this, 'markupObjectHandle'), 19 - $text); 20 - } 21 - 22 - protected function markupObjectHandle($matches) { 23 - // TODO: These are single gets but should be okay for now, they're behind 24 - // the cache. 25 - $phid = $this->loadObjectPHID($matches[1]); 26 - if (!$phid) { 27 - return $matches[0]; 28 - } 29 - 30 - $engine = $this->getEngine(); 31 - $token = $engine->storeText(''); 32 - 33 - $metadata_key = self::KEY_RULE_HANDLE; 34 - $metadata = $engine->getTextMetadata($metadata_key, array()); 35 - if (empty($metadata[$phid])) { 36 - $metadata[$phid] = array(); 37 - } 38 - $metadata[$phid][] = $token; 39 - $engine->setTextMetadata($metadata_key, $metadata); 40 - 41 - return $token; 42 - } 43 - 44 - public function didMarkupText() { 45 - $engine = $this->getEngine(); 46 - 47 - $metadata_key = self::KEY_RULE_HANDLE; 48 - $metadata = $engine->getTextMetadata($metadata_key, array()); 49 - if (empty($metadata)) { 50 - return; 51 - } 52 - 53 - $handles = id(new PhabricatorObjectHandleData(array_keys($metadata))) 54 - ->loadHandles(); 55 - 56 - foreach ($metadata as $phid => $tokens) { 57 - $link = $handles[$phid]->renderLink(); 58 - foreach ($tokens as $token) { 59 - $engine->overwriteStoredText($token, $link); 60 - } 61 - } 62 - 63 - $engine->setTextMetadata($metadata_key, array()); 64 - } 65 - 66 - }