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

When publishing project transactions, load ancestor members

Summary: Ref T10010. Fixes T10107. When we publish a transaction about a project, we perform visibility checks for many different users. We need to know all of the ancestors' members to perform these checks.

Test Plan:
- Before patch: when updating a subproject, daemons fatal trying to publish things because they can not test visibility of parent projects.
- After patch: daemons successfully publish subproject updates.
- Also added a unit test.

Reviewers: chad

Reviewed By: chad

Maniphest Tasks: T10010, T10107

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

+83 -28
+51 -18
src/applications/project/__tests__/PhabricatorProjectCoreTestCase.php
··· 113 113 $this->assertTrue($caught instanceof Exception); 114 114 } 115 115 116 + public function testAncestorMembers() { 117 + $user1 = $this->createUser(); 118 + $user1->save(); 119 + 120 + $user2 = $this->createUser(); 121 + $user2->save(); 122 + 123 + $parent = $this->createProject($user1); 124 + $child = $this->createProject($user1, $parent); 125 + 126 + $this->joinProject($child, $user1); 127 + $this->joinProject($child, $user2); 128 + 129 + $project = id(new PhabricatorProjectQuery()) 130 + ->setViewer($user1) 131 + ->withPHIDs(array($child->getPHID())) 132 + ->needAncestorMembers(true) 133 + ->executeOne(); 134 + 135 + $members = array_fuse($project->getParentProject()->getMemberPHIDs()); 136 + ksort($members); 137 + 138 + $expect = array_fuse( 139 + array( 140 + $user1->getPHID(), 141 + $user2->getPHID(), 142 + )); 143 + ksort($expect); 144 + 145 + $this->assertEqual($expect, $members); 146 + } 147 + 116 148 public function testAncestryQueries() { 117 149 $user = $this->createUser(); 118 150 $user->save(); ··· 577 609 $this->assertEqual($expect, $actual); 578 610 } 579 611 580 - private function attemptProjectEdit( 581 - PhabricatorProject $proj, 582 - PhabricatorUser $user, 583 - $skip_refresh = false) { 584 - 585 - $proj = $this->refreshProject($proj, $user, true); 586 - 587 - $new_name = $proj->getName().' '.mt_rand(); 588 - 589 - $xaction = new PhabricatorProjectTransaction(); 590 - $xaction->setTransactionType(PhabricatorProjectTransaction::TYPE_NAME); 591 - $xaction->setNewValue($new_name); 592 - 593 - $this->applyTransactions($proj, $user, array($xaction)); 594 - 595 - return true; 596 - } 597 - 598 612 public function testJoinLeaveProject() { 599 613 $user = $this->createUser(); 600 614 $user->save(); ··· 793 807 array($engineering, $engineering_scan), 794 808 pht('Engineering + Scan')); 795 809 } 810 + 811 + private function attemptProjectEdit( 812 + PhabricatorProject $proj, 813 + PhabricatorUser $user, 814 + $skip_refresh = false) { 815 + 816 + $proj = $this->refreshProject($proj, $user, true); 817 + 818 + $new_name = $proj->getName().' '.mt_rand(); 819 + 820 + $xaction = new PhabricatorProjectTransaction(); 821 + $xaction->setTransactionType(PhabricatorProjectTransaction::TYPE_NAME); 822 + $xaction->setNewValue($new_name); 823 + 824 + $this->applyTransactions($proj, $user, array($xaction)); 825 + 826 + return true; 827 + } 828 + 796 829 797 830 private function newTask( 798 831 PhabricatorUser $viewer,
+1 -1
src/applications/project/editor/PhabricatorProjectTransactionEditor.php
··· 582 582 return id(new PhabricatorProjectQuery()) 583 583 ->setViewer(PhabricatorUser::getOmnipotentUser()) 584 584 ->withPHIDs(array($object->getPHID())) 585 - ->needMembers(true) 585 + ->needAncestorMembers(true) 586 586 ->executeOne(); 587 587 } 588 588
+31 -9
src/applications/project/query/PhabricatorProjectQuery.php
··· 30 30 31 31 private $needSlugs; 32 32 private $needMembers; 33 + private $needAncestorMembers; 33 34 private $needWatchers; 34 35 private $needImages; 35 36 ··· 106 107 107 108 public function needMembers($need_members) { 108 109 $this->needMembers = $need_members; 110 + return $this; 111 + } 112 + 113 + public function needAncestorMembers($need_ancestor_members) { 114 + $this->needAncestorMembers = $need_ancestor_members; 109 115 return $this; 110 116 } 111 117 ··· 220 226 $types[] = $watcher_type; 221 227 } 222 228 229 + $all_graph = $this->getAllReachableAncestors($projects); 230 + 231 + if ($this->needAncestorMembers) { 232 + $src_projects = $all_graph; 233 + } else { 234 + $src_projects = $projects; 235 + } 236 + 223 237 $all_sources = array(); 224 - foreach ($projects as $project) { 238 + foreach ($src_projects as $project) { 225 239 if ($project->isMilestone()) { 226 240 $phid = $project->getParentProjectPHID(); 227 241 } else { ··· 234 248 ->withSourcePHIDs($all_sources) 235 249 ->withEdgeTypes($types); 236 250 251 + $need_all_edges = 252 + $this->needMembers || 253 + $this->needWatchers || 254 + $this->needAncestorMembers; 255 + 237 256 // If we only need to know if the viewer is a member, we can restrict 238 257 // the query to just their PHID. 239 258 $any_edges = true; 240 - if (!$this->needMembers && !$this->needWatchers) { 259 + if (!$need_all_edges) { 241 260 if ($viewer_phid) { 242 261 $edge_query->withDestinationPHIDs(array($viewer_phid)); 243 262 } else { ··· 253 272 } 254 273 255 274 $membership_projects = array(); 256 - foreach ($projects as $project) { 275 + foreach ($src_projects as $project) { 257 276 $project_phid = $project->getPHID(); 258 277 259 278 if ($project->isMilestone()) { ··· 274 293 $membership_projects[$project_phid] = $project; 275 294 } 276 295 277 - if ($this->needMembers) { 296 + if ($this->needMembers || $this->needAncestorMembers) { 278 297 $project->attachMemberPHIDs($member_phids); 279 298 } 280 299 ··· 289 308 } 290 309 } 291 310 292 - $all_graph = $this->getAllReachableAncestors($projects); 293 - $member_graph = $this->getAllReachableAncestors($membership_projects); 311 + // If we loaded ancestor members, we've already populated membership 312 + // lists above, so we can skip this step. 313 + if (!$this->needAncestorMembers) { 314 + $member_graph = $this->getAllReachableAncestors($membership_projects); 294 315 295 - foreach ($all_graph as $phid => $project) { 296 - $is_member = isset($member_graph[$phid]); 297 - $project->setIsUserMember($viewer_phid, $is_member); 316 + foreach ($all_graph as $phid => $project) { 317 + $is_member = isset($member_graph[$phid]); 318 + $project->setIsUserMember($viewer_phid, $is_member); 319 + } 298 320 } 299 321 300 322 return $projects;