@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 projects to review revisions

Summary:
Ref T1279. No actual logical changes, but:

- You can now add projects as reviewers from the revision view typeahead ("Add Reviewers" action).
- You can now add projects as reviewers from the revision detail typeahead.
- You can now add projects as reviewers from the CLI (`#yoloswag`).
- Generated commit messages now list project reviewers (`Reviewers: #yoloswag`).

I'll separate projects from users in the "Reviewers" tables in the next revision.

Test Plan:
- Added projects as reviewers using the web UI and CLI.
- Used `arc amend --show --revision Dnnn` to generate commit messages.
- Viewed revision with project reviewers in web UI.

Reviewers: btrahan

Reviewed By: btrahan

CC: aran

Maniphest Tasks: T1279

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

+113 -28
+57 -19
src/applications/differential/field/specification/DifferentialFieldSpecification.php
··· 779 779 return $this->parseCommitMessageObjectList($value, $mailables = false); 780 780 } 781 781 782 + protected function parseCommitMessageUserOrProjectList($value) { 783 + return $this->parseCommitMessageObjectList( 784 + $value, 785 + $mailables = false, 786 + $allow_partial = false, 787 + $projects = true); 788 + } 789 + 782 790 /** 783 791 * Parse a list of mailable objects into a canonical PHID list. 784 792 * ··· 813 821 814 822 $object_map = array(); 815 823 816 - $users = id(new PhabricatorUser())->loadAllWhere( 817 - '(username IN (%Ls))', 818 - $value); 824 + $project_names = array(); 825 + $other_names = array(); 826 + foreach ($value as $item) { 827 + if (preg_match('/^#/', $item)) { 828 + $project_names[$item] = ltrim(phutil_utf8_strtolower($item), '#').'/'; 829 + } else { 830 + $other_names[] = $item; 831 + } 832 + } 819 833 820 - $user_map = mpull($users, 'getPHID', 'getUsername'); 821 - foreach ($user_map as $username => $phid) { 822 - // Usernames may have uppercase letters in them. Put both names in the 823 - // map so we can try the original case first, so that username *always* 824 - // works in weird edge cases where some other mailable object collides. 825 - $object_map[$username] = $phid; 826 - $object_map[strtolower($username)] = $phid; 834 + if ($project_names) { 835 + // TODO: (T603) This should probably be policy-aware, although maybe not, 836 + // since we generally don't want to destroy data and it doesn't leak 837 + // anything? 838 + $projects = id(new PhabricatorProjectQuery()) 839 + ->setViewer(PhabricatorUser::getOmnipotentUser()) 840 + ->withPhrictionSlugs($project_names) 841 + ->execute(); 842 + 843 + $reverse_map = array_flip($project_names); 844 + foreach ($projects as $project) { 845 + $reverse_key = $project->getPhrictionSlug(); 846 + if (isset($reverse_map[$reverse_key])) { 847 + $object_map[$reverse_map[$reverse_key]] = $project->getPHID(); 848 + } 849 + } 827 850 } 828 851 829 - if ($include_mailables) { 830 - $mailables = id(new PhabricatorMetaMTAMailingList())->loadAllWhere( 831 - '(email IN (%Ls)) OR (name IN (%Ls))', 832 - $value, 833 - $value); 834 - $object_map += mpull($mailables, 'getPHID', 'getName'); 835 - $object_map += mpull($mailables, 'getPHID', 'getEmail'); 852 + if ($other_names) { 853 + $users = id(new PhabricatorUser())->loadAllWhere( 854 + '(username IN (%Ls))', 855 + $other_names); 856 + 857 + $user_map = mpull($users, 'getPHID', 'getUsername'); 858 + foreach ($user_map as $username => $phid) { 859 + // Usernames may have uppercase letters in them. Put both names in the 860 + // map so we can try the original case first, so that username *always* 861 + // works in weird edge cases where some other mailable object collides. 862 + $object_map[$username] = $phid; 863 + $object_map[strtolower($username)] = $phid; 864 + } 865 + 866 + if ($include_mailables) { 867 + $mailables = id(new PhabricatorMetaMTAMailingList())->loadAllWhere( 868 + '(email IN (%Ls)) OR (name IN (%Ls))', 869 + $other_names, 870 + $other_names); 871 + $object_map += mpull($mailables, 'getPHID', 'getName'); 872 + $object_map += mpull($mailables, 'getPHID', 'getEmail'); 873 + } 836 874 } 837 875 838 876 $invalid = array(); 839 877 $results = array(); 840 878 foreach ($value as $name) { 841 879 if (empty($object_map[$name])) { 842 - if (empty($object_map[strtolower($name)])) { 880 + if (empty($object_map[phutil_utf8_strtolower($name)])) { 843 881 $invalid[] = $name; 844 882 } else { 845 - $results[] = $object_map[strtolower($name)]; 883 + $results[] = $object_map[phutil_utf8_strtolower($name)]; 846 884 } 847 885 } else { 848 886 $results[] = $object_map[$name];
+8 -5
src/applications/differential/field/specification/DifferentialReviewersFieldSpecification.php
··· 15 15 } 16 16 17 17 public function renderLabelForRevisionView() { 18 - return 'Reviewers:'; 18 + return pht('Reviewers'); 19 19 } 20 20 21 21 public function renderValueForRevisionView() { ··· 144 144 ->setLabel(pht('Reviewers')) 145 145 ->setName('reviewers') 146 146 ->setUser($this->getUser()) 147 - ->setDatasource('/typeahead/common/users/') 147 + ->setDatasource('/typeahead/common/usersorprojects/') 148 148 ->setValue($reviewer_map) 149 149 ->setError($this->error); 150 150 } ··· 179 179 return null; 180 180 } 181 181 182 + $project_type = PhabricatorProjectPHIDTypeProject::TYPECONST; 183 + 182 184 $names = array(); 183 185 foreach ($this->reviewers as $phid) { 184 - $names[] = $this->getHandle($phid)->getName(); 186 + $names[] = $this->getHandle($phid)->getObjectName(); 185 187 } 186 188 187 189 return implode(', ', $names); ··· 195 197 } 196 198 197 199 public function parseValueFromCommitMessage($value) { 198 - return $this->parseCommitMessageUserList($value); 200 + return $this->parseCommitMessageUserOrProjectList($value); 199 201 } 200 202 201 203 public function shouldAppearOnRevisionList() { ··· 242 244 $handles = array_select_keys( 243 245 $handles, 244 246 array($this->getRevision()->getPrimaryReviewer())) + $handles; 245 - $names = mpull($handles, 'getName'); 247 + 248 + $names = mpull($handles, 'getObjectName'); 246 249 return 'Reviewers: '.implode(', ', $names); 247 250 } 248 251
+3 -1
src/applications/differential/query/DifferentialRevisionQuery.php
··· 914 914 $edges = id(new PhabricatorEdgeQuery()) 915 915 ->withSourcePHIDs(mpull($revisions, 'getPHID')) 916 916 ->withEdgeTypes(array($type_reviewer)) 917 + ->setOrder(PhabricatorEdgeQuery::ORDER_OLDEST_FIRST) 917 918 ->execute(); 918 919 919 920 foreach ($revisions as $revision) { ··· 923 924 $data[] = array( 924 925 'relation' => DifferentialRevision::RELATION_REVIEWER, 925 926 'objectPHID' => $dst_phid, 927 + 'reasonPHID' => null, 926 928 ); 927 929 } 928 930 ··· 1024 1026 ->withSourcePHIDs(mpull($revisions, 'getPHID')) 1025 1027 ->withEdgeTypes(array($edge_type)) 1026 1028 ->needEdgeData(true) 1029 + ->setOrder(PhabricatorEdgeQuery::ORDER_OLDEST_FIRST) 1027 1030 ->execute(); 1028 1031 1029 1032 foreach ($revisions as $revision) { 1030 1033 $revision_edges = $edges[$revision->getPHID()][$edge_type]; 1031 - 1032 1034 $reviewers = array(); 1033 1035 foreach ($revision_edges as $user_phid => $edge) { 1034 1036 $data = $edge['data'];
+2
src/applications/differential/storage/DifferentialRevision.php
··· 237 237 $reviewer_phids = PhabricatorEdgeQuery::loadDestinationPHIDs( 238 238 $this->getPHID(), 239 239 PhabricatorEdgeConfig::TYPE_DREV_HAS_REVIEWER); 240 + $reviewer_phids = array_reverse($reviewer_phids); 240 241 241 242 foreach ($reviewer_phids as $phid) { 242 243 $data[] = array( 243 244 'relation' => self::RELATION_REVIEWER, 244 245 'objectPHID' => $phid, 246 + 'reasonPHID' => null, 245 247 ); 246 248 } 247 249
+2 -2
src/applications/differential/view/DifferentialAddCommentView.php
··· 118 118 'add_reviewers' => 1, 119 119 'resign' => 1, 120 120 ), 121 - 'src' => '/typeahead/common/users/', 121 + 'src' => '/typeahead/common/usersorprojects/', 122 122 'value' => $this->reviewers, 123 123 'row' => 'add-reviewers', 124 124 'ondemand' => PhabricatorEnv::getEnvConfig('tokenizer.ondemand'), 125 125 'labels' => $add_reviewers_labels, 126 - 'placeholder' => pht('Type a user name...'), 126 + 'placeholder' => pht('Type a user or project name...'), 127 127 ), 128 128 'add-ccs-tokenizer' => array( 129 129 'actions' => array('add_ccs' => 1),
+4
src/applications/notification/PhabricatorNotificationQuery.php
··· 107 107 return $this->formatWhereClause($where); 108 108 } 109 109 110 + protected function getPagingValue($item) { 111 + return $item->getChronologicalKey(); 112 + } 113 + 110 114 }
+13
src/applications/phid/PhabricatorObjectHandle.php
··· 14 14 private $status = PhabricatorObjectHandleStatus::STATUS_OPEN; 15 15 private $complete; 16 16 private $disabled; 17 + private $objectName; 18 + 19 + public function setObjectName($object_name) { 20 + $this->objectName = $object_name; 21 + return $this; 22 + } 23 + 24 + public function getObjectName() { 25 + if (!$this->objectName) { 26 + return $this->getName(); 27 + } 28 + return $this->objectName; 29 + } 17 30 18 31 public function setURI($uri) { 19 32 $this->uri = $uri;
+1
src/applications/project/phid/PhabricatorProjectPHIDTypeProject.php
··· 39 39 $id = $project->getID(); 40 40 41 41 $handle->setName($name); 42 + $handle->setObjectName('#'.rtrim($project->getPhrictionSlug(), '/')); 42 43 $handle->setURI("/project/view/{$id}/"); 43 44 } 44 45 }
+23 -1
src/infrastructure/edges/query/PhabricatorEdgeQuery.php
··· 26 26 private $edgeTypes; 27 27 private $resultSet; 28 28 29 + const ORDER_OLDEST_FIRST = 'order:oldest'; 30 + const ORDER_NEWEST_FIRST = 'order:newest'; 31 + private $order = self::ORDER_NEWEST_FIRST; 32 + 29 33 private $needEdgeData; 30 34 31 35 ··· 70 74 */ 71 75 public function withEdgeTypes(array $types) { 72 76 $this->edgeTypes = $types; 77 + return $this; 78 + } 79 + 80 + 81 + /** 82 + * Configure the order edge results are returned in. 83 + * 84 + * @param const Order constant. 85 + * @return this 86 + * 87 + * @task config 88 + */ 89 + public function setOrder($order) { 90 + $this->order = $order; 73 91 return $this; 74 92 } 75 93 ··· 303 321 * @task internal 304 322 */ 305 323 private function buildOrderClause($conn_r) { 306 - return 'ORDER BY edge.dateCreated DESC, edge.seq ASC'; 324 + if ($this->order == self::ORDER_NEWEST_FIRST) { 325 + return 'ORDER BY edge.dateCreated DESC, edge.seq DESC'; 326 + } else { 327 + return 'ORDER BY edge.dateCreated ASC, edge.seq ASC'; 328 + } 307 329 } 308 330 309 331 }