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

Implement #yoloswag

Summary:
Ref T3190. #shipit

Likely future work:

- Extract project mentions from remarkup for ApplicationTransactions; this isn't relevant in any apps right now (but will be in Pholio before tooo long). Ref T3189.
- Allow projects to have alternate short names. As written, this is fine for most projects ("Differential" is `#differential`) but not so great for other projects ("Phabricator Public & Media Relations" is `#phabricator_public_media_relations`). This also breaks refs when you rename a project. Better would be letting long project names have short aliases (`#pr`) as permitted alternatives.

Since this mention uses `#` instead of a letter, I needed to do a small amount of regexp gymnastics.

Test Plan:
you only #yolo once

{F43615}

Reviewers: btrahan, chad

Reviewed By: btrahan

CC: aran

Maniphest Tasks: T3189, T3190

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

+71 -1
+2
src/__phutil_library_map__.php
··· 1714 1714 'PonderVotableView' => 'applications/ponder/view/PonderVotableView.php', 1715 1715 'PonderVoteEditor' => 'applications/ponder/editor/PonderVoteEditor.php', 1716 1716 'PonderVoteSaveController' => 'applications/ponder/controller/PonderVoteSaveController.php', 1717 + 'ProjectRemarkupRule' => 'applications/project/remarkup/ProjectRemarkupRule.php', 1717 1718 'QueryFormattingTestCase' => 'infrastructure/storage/__tests__/QueryFormattingTestCase.php', 1718 1719 'ReleephActiveProjectListView' => 'applications/releeph/view/project/list/ReleephActiveProjectListView.php', 1719 1720 'ReleephAuthorFieldSpecification' => 'applications/releeph/field/specification/ReleephAuthorFieldSpecification.php', ··· 3504 3505 'PonderVotableView' => 'AphrontView', 3505 3506 'PonderVoteEditor' => 'PhabricatorEditor', 3506 3507 'PonderVoteSaveController' => 'PonderController', 3508 + 'ProjectRemarkupRule' => 'PhabricatorRemarkupRuleObject', 3507 3509 'QueryFormattingTestCase' => 'PhabricatorTestCase', 3508 3510 'ReleephActiveProjectListView' => 'AphrontView', 3509 3511 'ReleephAuthorFieldSpecification' => 'ReleephFieldSpecification',
+6
src/applications/project/application/PhabricatorApplicationProject.php
··· 26 26 return self::GROUP_ORGANIZATION; 27 27 } 28 28 29 + public function getRemarkupRules() { 30 + return array( 31 + new ProjectRemarkupRule(), 32 + ); 33 + } 34 + 29 35 public function getRoutes() { 30 36 return array( 31 37 '/project/' => array(
+50
src/applications/project/remarkup/ProjectRemarkupRule.php
··· 1 + <?php 2 + 3 + /** 4 + * @group project 5 + */ 6 + final class ProjectRemarkupRule 7 + extends PhabricatorRemarkupRuleObject { 8 + 9 + protected function getObjectNamePrefix() { 10 + return '#'; 11 + } 12 + 13 + protected function getObjectIDPattern() { 14 + // NOTE: This explicitly does not match strings which contain only 15 + // digits, because digit strings like "#123" are used to reference tasks at 16 + // Facebook and are somewhat conventional in general. 17 + return '[^\s.!,:;]*[^\s\d.!,:;]+[^\s.!,:;]*'; 18 + } 19 + 20 + protected function loadObjects(array $ids) { 21 + $viewer = $this->getEngine()->getConfig('viewer'); 22 + 23 + // If the user types "#YoloSwag", we still want to match "#yoloswag", so 24 + // we normalize, query, and then map back to the original inputs. 25 + 26 + $map = array(); 27 + foreach ($ids as $key => $slug) { 28 + $map[PhabricatorSlug::normalize($slug)][] = $slug; 29 + } 30 + 31 + $projects = id(new PhabricatorProjectQuery()) 32 + ->setViewer($viewer) 33 + ->withPhrictionSlugs(array_keys($map)) 34 + ->execute(); 35 + 36 + $result = array(); 37 + foreach ($projects as $project) { 38 + $slugs = array($project->getPhrictionSlug()); 39 + foreach ($slugs as $slug) { 40 + foreach ($map[$slug] as $original) { 41 + $result[$original] = $project; 42 + } 43 + } 44 + } 45 + 46 + 47 + return $result; 48 + } 49 + 50 + }
+13 -1
src/infrastructure/markup/rule/PhabricatorRemarkupRuleObject.php
··· 99 99 array($this, 'markupObjectEmbed'), 100 100 $text); 101 101 102 + // If the prefix starts with a word character (like "D"), we want to 103 + // require a word boundary so that we don't match "XD1" as "D1". If the 104 + // prefix does not start with a word character, we want to require no word 105 + // boundary for the same reasons. Test if the prefix starts with a word 106 + // character. 107 + if (preg_match('/^\w/', $prefix)) { 108 + $boundary = '\\b'; 109 + } else { 110 + $boundary = '\\B'; 111 + } 112 + 102 113 // NOTE: The "(?<!#)" prevents us from linking "#abcdef" or similar. The 103 114 // "\b" allows us to link "(abcdef)" or similar without linking things 104 115 // in the middle of words. 116 + 105 117 $text = preg_replace_callback( 106 - '@(?<!#)\b'.$prefix.'('.$id.')(?:#([-\w\d]+))?\b@', 118 + '((?<!#)'.$boundary.$prefix.'('.$id.')(?:#([-\w\d]+))?\b)', 107 119 array($this, 'markupObjectReference'), 108 120 $text); 109 121